diff --git a/compiler/arch.ts b/compiler/arch.ts index da2ec53..7ada7dd 100644 --- a/compiler/arch.ts +++ b/compiler/arch.ts @@ -40,9 +40,17 @@ export type Builtins = typeof Builtins; export const Builtins = { StringConcat: 0x10, StringEqual: 0x11, - ArraySet: 0x20, + StringCharAt: 0x12, + StringLength: 0x13, + StringPushChar: 0x14, + ArrayNew: 0x20, + ArraySet: 0x21, + ArrayPush: 0x22, + ArrayAt: 0x23, + ArrayLength: 0x24, StructSet: 0x30, Print: 0x40, + } as const; export function opToString(op: number): string { @@ -112,16 +120,14 @@ export function opToString(op: number): string { export function builtinToString(builtin: number): string { switch (builtin) { - case Builtins.StringConcat: - return "StringConcat"; - case Builtins.StringEqual: - return "StringEqual"; - case Builtins.ArraySet: - return "ArraySet"; - case Builtins.StructSet: - return "StructSet"; - case Builtins.Print: - return "Print"; + case Builtins.StringConcat: return "StringConcat"; + case Builtins.StringEqual: return "StringEqual"; + case Builtins.StringCharAt: return "StringCharAt"; + case Builtins.StringLength: return "StringLength"; + case Builtins.StringPushChar: return "StringPushChar"; + case Builtins.ArraySet: return "ArraySet"; + case Builtins.StructSet: return "StructSet"; + case Builtins.Print: return "Print"; default: return ``; } diff --git a/compiler/assembler.ts b/compiler/assembler.ts index 361153d..07eca03 100644 --- a/compiler/assembler.ts +++ b/compiler/assembler.ts @@ -114,7 +114,7 @@ export class Assembler { }).join(", "); console.log(`${ip.toString().padStart(8, " ")}: ${op} ${args}`); ip += line.ins.map((lit) => - typeof lit === "string" ? lit.length : 1 + typeof lit === "string" ? lit.length + 1 : 1 ).reduce((acc, curr) => acc + curr, 0); } } diff --git a/compiler/checker.ts b/compiler/checker.ts index 8fcea55..fc950cf 100644 --- a/compiler/checker.ts +++ b/compiler/checker.ts @@ -119,16 +119,17 @@ export class Checker { if (stmt.kind.vtype!.type !== "fn") { throw new Error(); } - const { returnType } = stmt.kind.vtype!; - this.fnReturnStack.push(returnType); const isBuiltin = stmt.kind.anno && stmt.kind.anno.ident === "builtin"; if (isBuiltin) { - stmt.kind.body.kind = { type: "block", stmts: [] }; + return; } + + const { returnType } = stmt.kind.vtype!; + this.fnReturnStack.push(returnType); const body = this.checkExpr(stmt.kind.body); - this.fnReturnStack.pop(); + if (!vtypesEqual(returnType, body)) { this.report( `incompatible return type` + diff --git a/compiler/lexer.ts b/compiler/lexer.ts index 8bd4f48..982ac7f 100644 --- a/compiler/lexer.ts +++ b/compiler/lexer.ts @@ -1,6 +1,7 @@ import { Reporter } from "./info.ts"; import { Pos, Token } from "./token.ts"; + export class Lexer { private index = 0; private line = 1; diff --git a/compiler/lowerer.ts b/compiler/lowerer.ts index e939fde..80beeb5 100644 --- a/compiler/lowerer.ts +++ b/compiler/lowerer.ts @@ -128,10 +128,42 @@ export class Lowerer { } const value = anno.kind.value; switch (value) { - case "print": { + case "Print": { this.program.add(Ops.Builtin, Builtins.Print); break; } + case "StringLength": { + this.program.add(Ops.Builtin, Builtins.StringLength); + break; + } + case "StringCharAt": { + this.program.add(Ops.Builtin, Builtins.StringCharAt); + break; + } + case "StringPushChar": { + this.program.add(Ops.Builtin, Builtins.StringPushChar); + break; + } + case "ArrayNew": { + this.program.add(Ops.Builtin, Builtins.ArrayNew); + break; + } + case "ArraySet": { + this.program.add(Ops.Builtin, Builtins.ArraySet); + break; + } + case "ArrayPush": { + this.program.add(Ops.Builtin, Builtins.ArrayPush); + break; + } + case "ArrayLength": { + this.program.add(Ops.Builtin, Builtins.ArrayLength); + break; + } + case "ArrayAt": { + this.program.add(Ops.Builtin, Builtins.ArrayAt); + break; + } default: { throw new Error( `unrecognized builtin '${value}'`, @@ -279,6 +311,9 @@ export class Lowerer { case "+": this.program.add(Ops.Add); return; + case "-": + this.program.add(Ops.Subtract); + return; case "*": this.program.add(Ops.Multiply); return; diff --git a/compiler/parser.ts b/compiler/parser.ts index a3b3ab1..90c1f1a 100644 --- a/compiler/parser.ts +++ b/compiler/parser.ts @@ -429,7 +429,7 @@ export class Parser { left = this.parBinTail(left, pos, this.parseMulDiv, "+"); continue; } - if (this.test(".")) { + if (this.test("-")) { left = this.parBinTail(left, pos, this.parseMulDiv, "-"); continue; } diff --git a/examples/std.slg b/examples/std.slg new file mode 100644 index 0000000..549699f --- /dev/null +++ b/examples/std.slg @@ -0,0 +1,53 @@ + +fn array_new_string() -> [string] #[builtin(ArrayNew)] {} + +fn print(msg: string) #[builtin(Print)] {} + +fn array_push_string(array: [string], str: string) #[builtin(ArrayPush)] {} + +fn string_push_char(str: string, value: int) #[builtin(StringPushChar)] {} + +fn string_char_at(str: string, index: int) -> int #[builtin(StringCharAt)] {} + +fn string_length(str: string) -> int #[builtin(StringLength)] {} + +fn array_string_length(array: [string]) -> int #[builtin(StringLength)] {} + +fn array_string_at(array: [string], index: int) -> string #[builtin(ArrayAt)] {} + +fn char(ch: string) -> int { + string_char_at(ch, 0) +} + +fn split(str: string, seperator: int) -> [string] { + let result: [string] = array_new_string(); + + let i = 0; + let current_str = ""; + loop { + let char = string_char_at(str, i); + if char == seperator { + array_push_string(result, current_str); + current_str = ""; + } else { + string_push_char(current_str, char); + } + if string_length(str) - 1 == i { + break; + } + i = i + 1; + } + result +} + +fn main() { + let array = split("aoisfjasoifjsaiofjsa", char("a")); + let i = 0; + loop { + print(array_string_at(array, i) + "\n"); + i = i + 1; + if array_string_length(array) - 1 == i { + break; + } + } +} diff --git a/runtime/alloc.hpp b/runtime/alloc.hpp index 9d4e4e3..2fbf94b 100644 --- a/runtime/alloc.hpp +++ b/runtime/alloc.hpp @@ -33,6 +33,10 @@ template <> struct AllocTypeType { using Type = Struct; }; // clang-format on struct AllocItem { + AllocItem(Value&& val) : type(AllocType::Value), value(val) {} + AllocItem(Array&& val) : type(AllocType::Array), value(val) {} + AllocItem(Struct&& val) : type(AllocType::Struct), value(val) {} + template inline auto as() & -> AllocTypeType::Type& { return std::get::Type>(this->value); @@ -54,6 +58,26 @@ struct AllocItem { std::get::Type>(this->value)); } + inline auto as_value() & -> Value& { + return std::get(this->value); + } + inline auto as_value() const & -> const Value& { + return std::get(this->value); + } + inline auto as_array() & -> Array& { + return std::get(this->value); + } + inline auto as_array() const & -> const Array& { + return std::get(this->value); + } + inline auto as_struct() & -> Struct& { + return std::get(this->value); + } + inline auto as_struct() const & -> const Struct& { + return std::get(this->value); + } + + AllocType type; std::variant value; }; @@ -65,16 +89,23 @@ enum class ErrType { template struct Res { Res(T val) - : val(std::forward(val)) + : m_val(std::forward(val)) { } Res(ErrType err) - : err(err) + : m_err(err) { } - std::optional val; - std::optional err; + auto ok() const -> bool { return m_val.has_value(); } + auto val() & -> T& { return m_val.value(); } + auto val() const& -> const T& { return m_val.value(); } + auto val() && -> T&& { return std::move(m_val.value()); } + auto val() const&& -> const T&& { return std::move(m_val.value()); } + auto err() -> ErrType { return m_err.value(); } + + std::optional m_val; + std::optional m_err; }; class Heap { @@ -124,9 +155,9 @@ private: inline void move_item_to_other(uint32_t ptr) { auto res = at(ptr); - if (!res.val.has_value()) + if (!res.ok()) return; - auto val = res.val.value(); + auto val = res.val(); switch (val->type) { case AllocType::Value: { auto& v = val->as(); diff --git a/runtime/arch.hpp b/runtime/arch.hpp index 72b4b70..c2d5b4b 100644 --- a/runtime/arch.hpp +++ b/runtime/arch.hpp @@ -41,7 +41,14 @@ enum class Op : uint32_t { enum class Builtin : uint32_t { StringConcat = 0x10, StringEqual = 0x11, - ArraySet = 0x20, + StringCharAt = 0x12, + StringLength = 0x13, + StringPushChar = 0x14, + ArrayNew = 0x20, + ArraySet = 0x21, + ArrayPush = 0x22, + ArrayAt = 0x23, + ArrayLength = 0x24, StructSet = 0x30, Print = 0x40, }; diff --git a/runtime/json.hpp b/runtime/json.hpp index baffd4c..c22eca2 100644 --- a/runtime/json.hpp +++ b/runtime/json.hpp @@ -170,36 +170,7 @@ enum class TokTyp { Colon, }; -inline auto tok_typ_to_string(TokTyp typ) -> std::string -{ - 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"; - } - std::unreachable(); -} +auto tok_typ_to_string(TokTyp typ) -> std::string; struct Tok { Tok(TokTyp typ, Pos pos) diff --git a/runtime/main.cpp b/runtime/main.cpp index 1529b22..ce28b71 100644 --- a/runtime/main.cpp +++ b/runtime/main.cpp @@ -9,7 +9,7 @@ #include #include -bool print_stack_debug = false; +bool print_stack_debug = true; int execute_file_and_exit(std::string filename) { diff --git a/runtime/to_string.cpp b/runtime/to_string.cpp new file mode 100644 index 0000000..1fbfe0c --- /dev/null +++ b/runtime/to_string.cpp @@ -0,0 +1,76 @@ +#include "vm.hpp" +#include "json.hpp" + +using namespace sliger; + +auto sliger::maybe_op_to_string(uint32_t value) -> std::string +{ + switch (static_cast(value)) { + /* clang-format off */ + case Op::Nop: return "Nop"; + case Op::PushNull: return "PushNull"; + case Op::PushInt: return "PushInt"; + case Op::PushBool: return "PushBool"; + case Op::PushString: return "PushString"; + case Op::PushPtr: return "PushPtr"; + case Op::Pop: return "Pop"; + case Op::ReserveStatic: return "ReserveStatic"; + case Op::LoadStatic: return "LoadStatic"; + case Op::StoreStatic: return "StoreStatic"; + case Op::LoadLocal: return "LoadLocal"; + case Op::StoreLocal: return "StoreLocal"; + case Op::Call: return "Call"; + case Op::Return: return "Return"; + case Op::Jump: return "Jump"; + case Op::JumpIfTrue: return "JumpIfTrue"; + case Op::Builtin: return "Builtin"; + case Op::Add: return "Add"; + case Op::Subtract: return "Subtract"; + case Op::Multiply: return "Multiply"; + case Op::Divide: return "Divide"; + case Op::Remainder: return "Remainder"; + case Op::Equal: return "Equal"; + case Op::LessThan: return "LessThan"; + case Op::And: return "And"; + case Op::Or: return "Or"; + case Op::Xor: return "Xor"; + case Op::Not: return "Not"; + case Op::SourceMap: return "SourceMap"; + /* 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"; + } + std::unreachable(); +} \ No newline at end of file diff --git a/runtime/vm.cpp b/runtime/vm.cpp index 489c9db..503e77d 100644 --- a/runtime/vm.cpp +++ b/runtime/vm.cpp @@ -10,45 +10,6 @@ using namespace sliger; -inline auto maybe_op_to_string(uint32_t value) -> std::string -{ - switch (static_cast(value)) { - /* clang-format off */ - case Op::Nop: return "Nop"; - case Op::PushNull: return "PushNull"; - case Op::PushInt: return "PushInt"; - case Op::PushBool: return "PushBool"; - case Op::PushString: return "PushString"; - case Op::PushPtr: return "PushPtr"; - case Op::Pop: return "Pop"; - case Op::ReserveStatic: return "ReserveStatic"; - case Op::LoadStatic: return "LoadStatic"; - case Op::StoreStatic: return "StoreStatic"; - case Op::LoadLocal: return "LoadLocal"; - case Op::StoreLocal: return "StoreLocal"; - case Op::Call: return "Call"; - case Op::Return: return "Return"; - case Op::Jump: return "Jump"; - case Op::JumpIfTrue: return "JumpIfTrue"; - case Op::Builtin: return "Builtin"; - case Op::Add: return "Add"; - case Op::Subtract: return "Subtract"; - case Op::Multiply: return "Multiply"; - case Op::Divide: return "Divide"; - case Op::Remainder: return "Remainder"; - case Op::Equal: return "Equal"; - case Op::LessThan: return "LessThan"; - case Op::And: return "And"; - case Op::Or: return "Or"; - case Op::Xor: return "Xor"; - case Op::Not: return "Not"; - case Op::SourceMap: return "SourceMap"; - /* clang-format on */ - default: - return std::to_string(value); - } -} - void VM::run_until_done() { while (!done()) { @@ -164,8 +125,11 @@ void VM::run_instruction() stack_push(Ptr { .value = this->bp }); this->pc = fn_ptr.as_ptr().value; this->bp = static_cast(this->stack.size()); - for (size_t i = arguments.size(); i > 0; --i) { - stack_push(std::move(arguments.at(i - 1))); + // for (size_t i = arguments.size(); i > 0; --i) { + // stack_push(std::move(arguments.at(i - 1))); + // } + for (size_t i = 0; i < arguments.size(); ++i) { + stack_push(std::move(arguments.at(i))); } if (this->opts.flame_graph) { this->flame_graph.report_call( @@ -348,5 +312,59 @@ void VM::run_builtin(Builtin builtin_id) stack_push(Null()); break; } + case Builtin::StringCharAt: { + assert_stack_has(2); + auto str = stack_pop(); + auto index_value = stack_pop(); + auto index = static_cast(index_value.as_int().value); + auto ch = static_cast(str.as_string().value.at(index)); + stack_push(Int(ch)); + break; + } + case Builtin::StringLength: { + assert_stack_has(1); + auto str = stack_pop().as_string().value; + auto length = static_cast(str.length()); + stack_push(Int(length)); + break; + } + case Builtin::StringPushChar: { + assert_stack_has(2); + auto str = stack_pop(); + auto ch = stack_pop(); + auto new_str = std::string(str.as_string().value); + new_str.push_back(static_cast(ch.as_int().value)); + stack_push(String(new_str)); + break; + } + case Builtin::ArrayNew: { + auto alloc_res = this->heap.alloc(); + stack_push(Ptr(alloc_res.val())); + break; + } + case Builtin::ArrayPush: { + assert_stack_has(2); + auto array_ptr = stack_pop().as_ptr().value; + auto value = stack_pop(); + auto array = this->heap.at(array_ptr).val()->as_array(); + array.values.push_back(value); + stack_push(Null()); + break; + } + case Builtin::ArrayAt: { + assert_stack_has(2); + auto array_ptr = stack_pop().as_ptr().value; + auto index = stack_pop().as_int().value; + auto array = this->heap.at(array_ptr).val()->as_array(); + stack_push(array.values.at(static_cast(index))); + break; + } + case Builtin::ArrayLength: { + assert_stack_has(1); + auto array_ptr = stack_pop().as_ptr().value; + auto array = this->heap.at(array_ptr).val()->as_array(); + stack_push(Int(static_cast(array.values.size()))); + break; + } } } diff --git a/runtime/vm.hpp b/runtime/vm.hpp index f7ec40e..6adbf69 100644 --- a/runtime/vm.hpp +++ b/runtime/vm.hpp @@ -286,4 +286,6 @@ private: CodeCoverageBuilder code_coverage; }; +auto maybe_op_to_string(uint32_t value) -> std::string; + }