From 753788b06d26a6ffb5f444f070e27804ca5c5a00 Mon Sep 17 00:00:00 2001 From: sfja Date: Fri, 13 Dec 2024 20:05:27 +0100 Subject: [PATCH] add file support --- compiler/arch.ts | 6 ++ editors/vim/syntax/slige.vim | 2 +- examples/file.slg | 29 +++++++ examples/std.slg | 7 ++ runtime/Makefile | 27 ++++-- runtime/arch.hpp | 6 ++ runtime/to_string.cpp | 75 +++++++++++------ runtime/vm.cpp | 156 +++++++++++++++++++++++++++++++++-- runtime/vm.hpp | 12 +++ 9 files changed, 274 insertions(+), 46 deletions(-) create mode 100644 examples/file.slg diff --git a/compiler/arch.ts b/compiler/arch.ts index 1c297e7..e8f9aee 100644 --- a/compiler/arch.ts +++ b/compiler/arch.ts @@ -52,6 +52,12 @@ export const Builtins = { ArrayLength: 0x24, StructSet: 0x30, Print: 0x40, + FileOpen: 0x41, + FileClose: 0x42, + FileWriteString: 0x43, + FileReadToString: 0x44, + FileFlush: 0x45, + FileEof: 0x46, } as const; export function opToString(op: number): string { diff --git a/editors/vim/syntax/slige.vim b/editors/vim/syntax/slige.vim index 188c068..69fd2c8 100644 --- a/editors/vim/syntax/slige.vim +++ b/editors/vim/syntax/slige.vim @@ -9,7 +9,7 @@ endif syn keyword Keyword break return let fn loop if else struct import or and not syn keyword Special null -syn keyword Type int string +syn keyword Type int string bool syn keyword Boolean true false syn match Operator '+' diff --git a/examples/file.slg b/examples/file.slg new file mode 100644 index 0000000..a436e66 --- /dev/null +++ b/examples/file.slg @@ -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); +} + diff --git a/examples/std.slg b/examples/std.slg index fc55225..24bf7e9 100644 --- a/examples/std.slg +++ b/examples/std.slg @@ -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_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 { string_char_at(ch, 0) } diff --git a/runtime/Makefile b/runtime/Makefile index 67e31b4..b9b0777 100644 --- a/runtime/Makefile +++ b/runtime/Makefile @@ -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 = \ -std=c++23 \ -fsanitize=address,undefined \ -Og \ -pedantic -pedantic-errors \ - -Wall -Wextra -Wpedantic -Wconversion -Werror \ + -Wall -Wextra -Wpedantic -Wconversion -Werror\ +endif + +CXX = g++ 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 = g++ - all: build_dir $(OUT) $(OUT): $(CXX_OBJECTS) diff --git a/runtime/arch.hpp b/runtime/arch.hpp index c484a41..115f2f3 100644 --- a/runtime/arch.hpp +++ b/runtime/arch.hpp @@ -53,6 +53,12 @@ enum class Builtin : uint32_t { ArrayLength = 0x24, StructSet = 0x30, Print = 0x40, + FileOpen = 0x41, + FileClose = 0x42, + FileWriteString = 0x43, + FileReadToString = 0x44, + FileFlush = 0x45, + FileEof = 0x46, }; } diff --git a/runtime/to_string.cpp b/runtime/to_string.cpp index 1fbfe0c..a93ae7d 100644 --- a/runtime/to_string.cpp +++ b/runtime/to_string.cpp @@ -1,5 +1,6 @@ -#include "vm.hpp" #include "json.hpp" +#include "vm.hpp" +#include 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(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 { using namespace json; - + switch (typ) { - case TokTyp::Eof: - return "Eof"; - case TokTyp::String: - return "String"; - case TokTyp::Float: - return "Float"; - case TokTyp::False: - return "False"; - case TokTyp::True: - return "True"; - case TokTyp::Null: - return "Null"; - case TokTyp::LBrace: - return "LBrace"; - 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"; + /* clang-format off */ + case TokTyp::Eof: return "Eof"; + case TokTyp::String: return "String"; + case TokTyp::Float: return "Float"; + case TokTyp::False: return "False"; + case TokTyp::True: return "True"; + case TokTyp::Null: return "Null"; + case TokTyp::LBrace: return "LBrace"; + 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"; + /* clang-format on */ } std::unreachable(); -} \ No newline at end of file +} diff --git a/runtime/vm.cpp b/runtime/vm.cpp index dd9b412..ef9aa5d 100644 --- a/runtime/vm.cpp +++ b/runtime/vm.cpp @@ -1,6 +1,7 @@ #include "vm.hpp" #include "arch.hpp" #include +#include #include #include #include @@ -131,7 +132,7 @@ void VM::run_instruction() this->pc = fn_ptr.as_ptr().value; this->bp = static_cast(this->stack.size()); for (auto&& arg = arguments.rbegin(); arg != arguments.rend(); - ++arg) { + ++arg) { stack_push(*arg); } if (this->opts.flame_graph) { @@ -281,8 +282,8 @@ void VM::run_instruction() void VM::run_builtin(Builtin builtin_id) { if (this->opts.print_stack_debug) { - std::cout << std::format( - "Running builtin {}\n", static_cast(builtin_id)); + std::cout << std::format("Running builtin {}\n", + maybe_builtin_to_string(static_cast(builtin_id))); } switch (builtin_id) { case Builtin::IntToString: { @@ -293,6 +294,45 @@ void VM::run_builtin(Builtin builtin_id) 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: { assert_stack_has(2); auto right = stack_pop(); @@ -342,7 +382,13 @@ void VM::run_builtin(Builtin builtin_id) stack_push(Int(number)); break; } - + default: + break; + } +} +void VM::run_array_builtin(Builtin builtin_id) +{ + switch (builtin_id) { case Builtin::ArrayNew: { auto alloc_res = this->heap.alloc(); stack_push(Ptr(alloc_res.val())); @@ -384,12 +430,14 @@ void VM::run_builtin(Builtin builtin_id) stack_push(Int(static_cast(array.values.size()))); break; } - case Builtin::StructSet: { - assert_stack_has(2); - std::cerr << std::format("not implemented\n"); - std::exit(1); + default: break; - } + } +} + +void VM::run_file_builtin(Builtin builtin_id) +{ + switch (builtin_id) { case Builtin::Print: { assert_stack_has(1); auto message = stack_pop().as_string().value; @@ -397,8 +445,98 @@ void VM::run_builtin(Builtin builtin_id) stack_push(Null()); 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 result = std::string(); diff --git a/runtime/vm.hpp b/runtime/vm.hpp index 04e29d5..206ebc6 100644 --- a/runtime/vm.hpp +++ b/runtime/vm.hpp @@ -6,6 +6,7 @@ #include "value.hpp" #include #include +#include #include #include @@ -175,6 +176,9 @@ public: private: 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; } @@ -243,6 +247,13 @@ private: SourcePos current_pos = { 0, 1, 1 }; int64_t instruction_counter = 0; + int32_t file_id_counter = 3; + std::unordered_map open_files { + { 0, stdin }, + { 1, stdout }, + { 2, stderr }, + }; + bool halt = false; FlameGraphBuilder flame_graph; @@ -250,5 +261,6 @@ private: }; auto maybe_op_to_string(uint32_t value) -> std::string; +auto maybe_builtin_to_string(uint32_t value) -> std::string; }