add file support
This commit is contained in:
parent
70473f353a
commit
753788b06d
@ -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 {
|
||||||
|
@ -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
29
examples/file.slg
Normal 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);
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
}
|
}
|
156
runtime/vm.cpp
156
runtime/vm.cpp
@ -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>
|
||||||
@ -131,7 +132,7 @@ void VM::run_instruction()
|
|||||||
this->pc = fn_ptr.as_ptr().value;
|
this->pc = fn_ptr.as_ptr().value;
|
||||||
this->bp = static_cast<uint32_t>(this->stack.size());
|
this->bp = static_cast<uint32_t>(this->stack.size());
|
||||||
for (auto&& arg = arguments.rbegin(); arg != arguments.rend();
|
for (auto&& arg = arguments.rbegin(); arg != arguments.rend();
|
||||||
++arg) {
|
++arg) {
|
||||||
stack_push(*arg);
|
stack_push(*arg);
|
||||||
}
|
}
|
||||||
if (this->opts.flame_graph) {
|
if (this->opts.flame_graph) {
|
||||||
@ -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();
|
||||||
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user