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,
StructSet: 0x30,
Print: 0x40,
FileOpen: 0x41,
FileClose: 0x42,
FileWriteString: 0x43,
FileReadToString: 0x44,
FileFlush: 0x45,
FileEof: 0x46,
} as const;
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 Special null
syn keyword Type int string
syn keyword Type int string bool
syn keyword Boolean true false
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_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)
}

View File

@ -1,12 +1,20 @@
# 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 \
@ -14,6 +22,9 @@ CXX_FLAGS = \
-pedantic -pedantic-errors \
-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)

View File

@ -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,
};
}

View File

@ -1,5 +1,6 @@
#include "vm.hpp"
#include "json.hpp"
#include "vm.hpp"
#include <string>
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
{
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();
}

View File

@ -1,6 +1,7 @@
#include "vm.hpp"
#include "arch.hpp"
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <format>
#include <iostream>
@ -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<uint32_t>(builtin_id));
std::cout << std::format("Running builtin {}\n",
maybe_builtin_to_string(static_cast<uint32_t>(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<heap::AllocType::Array>();
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())));
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();

View File

@ -6,6 +6,7 @@
#include "value.hpp"
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <string>
#include <vector>
@ -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<int32_t, FILE*> 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;
}