add file support

This commit is contained in:
sfja 2024-12-13 20:05:27 +01:00
parent 70473f353a
commit 753788b06d
9 changed files with 274 additions and 46 deletions

View File

@ -52,6 +52,12 @@ export const Builtins = {
ArrayLength: 0x24, ArrayLength: 0x24,
StructSet: 0x30, StructSet: 0x30,
Print: 0x40, Print: 0x40,
FileOpen: 0x41,
FileClose: 0x42,
FileWriteString: 0x43,
FileReadToString: 0x44,
FileFlush: 0x45,
FileEof: 0x46,
} as const; } as const;
export function opToString(op: number): string { export function opToString(op: number): string {

View File

@ -9,7 +9,7 @@ endif
syn keyword Keyword break return let fn loop if else struct import or and not syn keyword Keyword break return let fn loop if else struct import or and not
syn keyword Special null syn keyword Special null
syn keyword Type int string syn keyword Type int string bool
syn keyword Boolean true false syn keyword Boolean true false
syn match Operator '+' syn match Operator '+'

29
examples/file.slg Normal file
View File

@ -0,0 +1,29 @@
// let stdin = 0;
// let stdout = 1;
// let stderr = 2;
fn print(msg: string) #[builtin(Print)] {}
fn println(msg: string) { print(msg + "\n") }
fn file_open(filename: string, mode: string) -> int #[builtin(FileOpen)] {}
fn file_close(file: int) #[builtin(FileClose)] {}
fn file_write_string(file: int, content: string) -> int #[builtin(FileWriteString)] {}
fn file_read_to_string(file: int) -> string #[builtin(FileReadToString)] {}
fn file_flush(file: int) #[builtin(FileFlush)] {}
fn file_eof(file: int) -> bool #[builtin(FileEof)] {}
fn main() {
let file = file_open("test.txt", "w");
file_write_string(file, "hello world");
file_close(file);
file = file_open("test.txt", "r");
let content = file_read_to_string(file);
file_close(file);
file_write_string(1, content + "\n");
file_flush(1);
}

View File

@ -12,6 +12,13 @@ fn array_push_string(array: [string], value: string) #[builtin(ArrayPush)] {}
fn array_length_string(array: [string]) -> int #[builtin(ArrayLength)] {} fn array_length_string(array: [string]) -> int #[builtin(ArrayLength)] {}
fn array_at_string(array: [string], index: int) -> string #[builtin(ArrayAt)] {} fn array_at_string(array: [string], index: int) -> string #[builtin(ArrayAt)] {}
fn file_open(filename: string, mode: string) -> int #[builtin(FileOpen)] {}
fn file_close(file: int) #[builtin(FileClose)] {}
fn file_write_string(file: int, content: string) -> int #[builtin(FileWriteString)] {}
fn file_read_to_string(file: int) -> string #[builtin(FileReadToString)] {}
fn file_flush(file: int) #[builtin(FileFlush)] {}
fn file_eof(file: int) -> bool #[builtin(FileEof)] {}
fn char(ch: string) -> int { fn char(ch: string) -> int {
string_char_at(ch, 0) string_char_at(ch, 0)
} }

View File

@ -1,19 +1,30 @@
# CXX_FLAGS = \
# -std=c++23 \
# -Og \
# -fsanitize=address,undefined \
# -pedantic -pedantic-errors \
# -Wall -Wextra -Wpedantic -Wconversion -Werror \
MAKEFLAGS := --jobs=$(shell nproc)
RELEASE=0
ifeq ($(RELEASE),1)
CXX_FLAGS = \
-std=c++23 \
-O2 \
-pedantic -pedantic-errors \
-Wall -Wextra -Wpedantic -Wconversion -Werror\
else
CXX_FLAGS = \ CXX_FLAGS = \
-std=c++23 \ -std=c++23 \
-fsanitize=address,undefined \ -fsanitize=address,undefined \
-Og \ -Og \
-pedantic -pedantic-errors \ -pedantic -pedantic-errors \
-Wall -Wextra -Wpedantic -Wconversion -Werror \ -Wall -Wextra -Wpedantic -Wconversion -Werror\
endif
CXX = g++
OUT=build/sliger OUT=build/sliger
@ -23,8 +34,6 @@ CXX_SOURCES = $(shell find . -name "*.cpp" -type f -printf '%P\n')
CXX_OBJECTS = $(patsubst %.cpp,build/%.o,$(CXX_SOURCES)) CXX_OBJECTS = $(patsubst %.cpp,build/%.o,$(CXX_SOURCES))
CXX = g++
all: build_dir $(OUT) all: build_dir $(OUT)
$(OUT): $(CXX_OBJECTS) $(OUT): $(CXX_OBJECTS)

View File

@ -53,6 +53,12 @@ enum class Builtin : uint32_t {
ArrayLength = 0x24, ArrayLength = 0x24,
StructSet = 0x30, StructSet = 0x30,
Print = 0x40, Print = 0x40,
FileOpen = 0x41,
FileClose = 0x42,
FileWriteString = 0x43,
FileReadToString = 0x44,
FileFlush = 0x45,
FileEof = 0x46,
}; };
} }

View File

@ -1,5 +1,6 @@
#include "vm.hpp"
#include "json.hpp" #include "json.hpp"
#include "vm.hpp"
#include <string>
using namespace sliger; using namespace sliger;
@ -42,35 +43,55 @@ auto sliger::maybe_op_to_string(uint32_t value) -> std::string
} }
} }
auto sliger::maybe_builtin_to_string(uint32_t value) -> std::string
{
switch (static_cast<Builtin>(value)) {
/* clang-format off */
case Builtin::IntToString: return "IntToString";
case Builtin::StringConcat: return "StringConcat";
case Builtin::StringEqual: return "StringEqual";
case Builtin::StringCharAt: return "StringCharAt";
case Builtin::StringLength: return "StringLength";
case Builtin::StringPushChar: return "StringPushChar";
case Builtin::StringToInt: return "StringToInt";
case Builtin::ArrayNew: return "ArrayNew";
case Builtin::ArraySet: return "ArraySet";
case Builtin::ArrayPush: return "ArrayPush";
case Builtin::ArrayAt: return "ArrayAt";
case Builtin::ArrayLength: return "ArrayLength";
case Builtin::StructSet: return "StructSet";
case Builtin::Print: return "Print";
case Builtin::FileOpen: return "FileOpen";
case Builtin::FileClose: return "FileClose";
case Builtin::FileWriteString: return "FileWrite";
case Builtin::FileReadToString: return "FileReadToString";
case Builtin::FileFlush: return "FileFlush";
case Builtin::FileEof: return "FileEof";
/* clang-format on */
default:
return std::to_string(value);
}
}
auto json::tok_typ_to_string(json::TokTyp typ) -> std::string auto json::tok_typ_to_string(json::TokTyp typ) -> std::string
{ {
using namespace json; using namespace json;
switch (typ) { switch (typ) {
case TokTyp::Eof: /* clang-format off */
return "Eof"; case TokTyp::Eof: return "Eof";
case TokTyp::String: case TokTyp::String: return "String";
return "String"; case TokTyp::Float: return "Float";
case TokTyp::Float: case TokTyp::False: return "False";
return "Float"; case TokTyp::True: return "True";
case TokTyp::False: case TokTyp::Null: return "Null";
return "False"; case TokTyp::LBrace: return "LBrace";
case TokTyp::True: case TokTyp::RBrace: return "RBrace";
return "True"; case TokTyp::LBracket: return "LBracket";
case TokTyp::Null: case TokTyp::RBracket: return "RBracket";
return "Null"; case TokTyp::Comma: return "Comma";
case TokTyp::LBrace: case TokTyp::Colon: return "Colon";
return "LBrace"; /* clang-format on */
case TokTyp::RBrace:
return "RBrace";
case TokTyp::LBracket:
return "LBracket";
case TokTyp::RBracket:
return "RBracket";
case TokTyp::Comma:
return "Comma";
case TokTyp::Colon:
return "Colon";
} }
std::unreachable(); std::unreachable();
} }

View File

@ -1,6 +1,7 @@
#include "vm.hpp" #include "vm.hpp"
#include "arch.hpp" #include "arch.hpp"
#include <cstdint> #include <cstdint>
#include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <format> #include <format>
#include <iostream> #include <iostream>
@ -281,8 +282,8 @@ void VM::run_instruction()
void VM::run_builtin(Builtin builtin_id) void VM::run_builtin(Builtin builtin_id)
{ {
if (this->opts.print_stack_debug) { if (this->opts.print_stack_debug) {
std::cout << std::format( std::cout << std::format("Running builtin {}\n",
"Running builtin {}\n", static_cast<uint32_t>(builtin_id)); maybe_builtin_to_string(static_cast<uint32_t>(builtin_id)));
} }
switch (builtin_id) { switch (builtin_id) {
case Builtin::IntToString: { case Builtin::IntToString: {
@ -293,6 +294,45 @@ void VM::run_builtin(Builtin builtin_id)
break; break;
} }
case Builtin::StringConcat:
case Builtin::StringEqual:
case Builtin::StringCharAt:
case Builtin::StringLength:
case Builtin::StringPushChar:
case Builtin::StringToInt:
run_string_builtin(builtin_id);
break;
case Builtin::ArrayNew:
case Builtin::ArraySet:
case Builtin::ArrayPush:
case Builtin::ArrayAt:
case Builtin::ArrayLength:
run_array_builtin(builtin_id);
break;
case Builtin::StructSet: {
assert_stack_has(2);
std::cerr << std::format("not implemented\n");
std::exit(1);
break;
}
case Builtin::Print:
case Builtin::FileOpen:
case Builtin::FileClose:
case Builtin::FileWriteString:
case Builtin::FileReadToString:
case Builtin::FileFlush:
case Builtin::FileEof:
run_file_builtin(builtin_id);
break;
}
}
void VM::run_string_builtin(Builtin builtin_id)
{
switch (builtin_id) {
case Builtin::StringConcat: { case Builtin::StringConcat: {
assert_stack_has(2); assert_stack_has(2);
auto right = stack_pop(); auto right = stack_pop();
@ -342,7 +382,13 @@ void VM::run_builtin(Builtin builtin_id)
stack_push(Int(number)); stack_push(Int(number));
break; break;
} }
default:
break;
}
}
void VM::run_array_builtin(Builtin builtin_id)
{
switch (builtin_id) {
case Builtin::ArrayNew: { case Builtin::ArrayNew: {
auto alloc_res = this->heap.alloc<heap::AllocType::Array>(); auto alloc_res = this->heap.alloc<heap::AllocType::Array>();
stack_push(Ptr(alloc_res.val())); stack_push(Ptr(alloc_res.val()));
@ -384,12 +430,14 @@ void VM::run_builtin(Builtin builtin_id)
stack_push(Int(static_cast<int32_t>(array.values.size()))); stack_push(Int(static_cast<int32_t>(array.values.size())));
break; break;
} }
case Builtin::StructSet: { default:
assert_stack_has(2);
std::cerr << std::format("not implemented\n");
std::exit(1);
break; break;
} }
}
void VM::run_file_builtin(Builtin builtin_id)
{
switch (builtin_id) {
case Builtin::Print: { case Builtin::Print: {
assert_stack_has(1); assert_stack_has(1);
auto message = stack_pop().as_string().value; auto message = stack_pop().as_string().value;
@ -397,8 +445,98 @@ void VM::run_builtin(Builtin builtin_id)
stack_push(Null()); stack_push(Null());
break; break;
} }
case Builtin::FileOpen: {
assert_stack_has(2);
auto mode = stack_pop().as_string().value;
auto filename = stack_pop().as_string().value;
FILE* fp = std::fopen(filename.c_str(), mode.c_str());
if (fp == nullptr) {
stack_push(Int(0));
break;
}
auto file_id = this->file_id_counter;
this->file_id_counter += 1;
this->open_files.insert_or_assign(file_id, fp);
stack_push(Int(file_id));
break;
}
case Builtin::FileClose: {
assert_stack_has(2);
auto file_id = stack_pop().as_int().value;
auto fp = this->open_files.find(file_id);
if (fp != this->open_files.end()) {
std::fclose(fp->second);
this->open_files.erase(file_id);
}
stack_push(Null());
break;
}
case Builtin::FileWriteString: {
assert_stack_has(2);
auto content = stack_pop().as_string().value;
auto file_id = stack_pop().as_int().value;
auto fp = this->open_files.find(file_id);
if (fp == this->open_files.end()) {
std::cerr << std::format("error: no open file {}\n", file_id);
std::exit(1);
}
auto res = std::fputs(content.c_str(), fp->second);
if (res <= 0) {
stack_push(Int(-1));
break;
}
stack_push(Int(0));
break;
}
case Builtin::FileReadToString: {
assert_stack_has(1);
auto file_id = stack_pop().as_int().value;
auto fp = this->open_files.find(file_id);
if (fp == this->open_files.end()) {
std::cerr << std::format("error: no open file {}\n", file_id);
std::exit(1);
}
auto content = std::string();
while (true) {
constexpr size_t buf_size = 128;
char buf[buf_size] = "";
auto res = std::fread(buf, 1, buf_size, fp->second);
if (res == 0) {
break;
}
content.append(std::string(buf));
}
stack_push(String(std::move(content)));
break;
}
case Builtin::FileFlush: {
assert_stack_has(1);
auto file_id = stack_pop().as_int().value;
auto fp = this->open_files.find(file_id);
if (fp == this->open_files.end()) {
std::cerr << std::format("error: no open file {}\n", file_id);
std::exit(1);
}
std::fflush(fp->second);
stack_push(Null());
break;
}
case Builtin::FileEof: {
assert_stack_has(1);
auto file_id = stack_pop().as_int().value;
auto fp = this->open_files.find(file_id);
if (fp == this->open_files.end()) {
std::cerr << std::format("error: no open file {}\n", file_id);
std::exit(1);
}
stack_push(Bool(std::feof(fp->second) != 0));
break;
}
default:
break;
} }
} }
auto VM::stack_repr_string(size_t max_items) const -> std::string auto VM::stack_repr_string(size_t max_items) const -> std::string
{ {
auto result = std::string(); auto result = std::string();

View File

@ -6,6 +6,7 @@
#include "value.hpp" #include "value.hpp"
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <cstdio>
#include <string> #include <string>
#include <vector> #include <vector>
@ -175,6 +176,9 @@ public:
private: private:
void run_builtin(Builtin builtin_id); void run_builtin(Builtin builtin_id);
void run_string_builtin(Builtin builtin_id);
void run_array_builtin(Builtin builtin_id);
void run_file_builtin(Builtin builtin_id);
inline void step() { this->pc += 1; } inline void step() { this->pc += 1; }
@ -243,6 +247,13 @@ private:
SourcePos current_pos = { 0, 1, 1 }; SourcePos current_pos = { 0, 1, 1 };
int64_t instruction_counter = 0; int64_t instruction_counter = 0;
int32_t file_id_counter = 3;
std::unordered_map<int32_t, FILE*> open_files {
{ 0, stdin },
{ 1, stdout },
{ 2, stderr },
};
bool halt = false; bool halt = false;
FlameGraphBuilder flame_graph; FlameGraphBuilder flame_graph;
@ -250,5 +261,6 @@ private:
}; };
auto maybe_op_to_string(uint32_t value) -> std::string; auto maybe_op_to_string(uint32_t value) -> std::string;
auto maybe_builtin_to_string(uint32_t value) -> std::string;
} }