From 3adbcab494b6f509fba378eeb13adb4748c4867c Mon Sep 17 00:00:00 2001 From: Judah Caruso Date: Thu, 17 Jul 2025 08:31:29 -0600 Subject: [PATCH] move vm to x --- vm/module.jai | 94 ------------------- {vm => x/vm}/interp.jai | 21 ++--- x/vm/module.jai | 202 ++++++++++++++++++++++++++++++++++++++++ {vm => x/vm}/parser.jai | 91 +++++++++--------- x/vm/resolver.jai | 2 + 5 files changed, 259 insertions(+), 151 deletions(-) delete mode 100644 vm/module.jai rename {vm => x/vm}/interp.jai (94%) create mode 100644 x/vm/module.jai rename {vm => x/vm}/parser.jai (91%) create mode 100644 x/vm/resolver.jai diff --git a/vm/module.jai b/vm/module.jai deleted file mode 100644 index 151495d..0000000 --- a/vm/module.jai +++ /dev/null @@ -1,94 +0,0 @@ -// #module_parameters(RUN_TESTS := false); - -#load "parser.jai"; -#load "interp.jai"; - -#scope_module; - -// exported to the entire module since we want these everywhere - -mem :: #import "jc/memory"; -array :: #import "jc/array"; -kv :: #import "jc/kv"; - -basic :: #import "Basic"; // @future -strings :: #import "String"; // @future - -#scope_file; - -#run { - parser: Parser; - ok := parse_string(*parser, #string END - fn add(l, r) do return l + r end - fn sub(l, r) do return l - r end - fn mul(l, r) do return l * r end - fn div(l, r) do return l / r end - - var x = 21.0 - var y = 22.0 - var z = x + y - - x = x + 1.0 / 2.0 - print x - - x = add(x, div(1.0, 2.0)) - print x - - print x == x - print x == y - print x == z - - assert x == y - assert x != z - - // def ( - // Add = poly[T] proc(x T, y T) T do return x + y end - // Sub = poly[T] proc(x T, y T) T do return x - y end - // Mul = poly[T] proc(x T, y T) T do return x * y end - // Div = poly[T] proc(x T, y T) T do return x / y end - // ) - - // def ( - // Addi = Add[int] - // Addf = Add[float] - // Subi = Sub[int] - // Subf = Sub[float] - // Muli = Mul[int] - // Mulf = Mul[float] - // Divi = Div[int] - // Divf = Div[float] - // ) - - // def Foo = struct { - // x int = 1 - // y int = 2 - // z int = 3 - // } - - // def Value = union { - // i int - // f float - // b bool - // } - - // def Kind = enum { - // a - // b - // c - // d - // } - - // var foo = Foo{ x = 10, y = 20, z = 30 } - // var val = Value{ f = 3.14 } - // var kind = Kind.a - END); - - interp: Interp; - interp.toplevel = parser.toplevel; - - interp_program(*interp); -} - -// #if RUN_TESTS { -// test :: #import "jc/test"; -// } diff --git a/vm/interp.jai b/x/vm/interp.jai similarity index 94% rename from vm/interp.jai rename to x/vm/interp.jai index 86b2270..b6bab2d 100644 --- a/vm/interp.jai +++ b/x/vm/interp.jai @@ -1,10 +1,3 @@ -Interp :: struct { - allocator: Allocator; - - toplevel: []*Node; - global: *Interp_Scope; -} - Interp_Scope :: struct { parent: *Interp_Scope; bindings: kv.Kv(string, *Interp_Value); @@ -36,7 +29,7 @@ Interp_Value :: struct { } } -interp_program :: (i: *Interp) { +interp_program :: (i: *Vm) { try_lazy_init(i); for i.toplevel { @@ -44,7 +37,7 @@ interp_program :: (i: *Interp) { } } -interp_statement :: (i: *Interp, stmt: *Node, scope: *Interp_Scope) { +interp_statement :: (i: *Vm, stmt: *Node, scope: *Interp_Scope) { if stmt.kind == { case .variable; var := stmt.(*Node_Var); @@ -112,7 +105,7 @@ interp_statement :: (i: *Interp, stmt: *Node, scope: *Interp_Scope) { } } -interp_lvalue :: (i: *Interp, expr: *Node, scope: *Interp_Scope) -> *Interp_Value { +interp_lvalue :: (i: *Vm, expr: *Node, scope: *Interp_Scope) -> *Interp_Value { if expr.kind == { case .symbol; sym := expr.(*Node_Symbol); @@ -128,7 +121,7 @@ interp_lvalue :: (i: *Interp, expr: *Node, scope: *Interp_Scope) -> *Interp_Valu return null; } -interp_expression :: (i: *Interp, expr: *Node, scope: *Interp_Scope) -> *Interp_Value { +interp_expression :: (i: *Vm, expr: *Node, scope: *Interp_Scope) -> *Interp_Value { if expr.kind == { case .procedure_call; call := expr.(*Node_Procedure_Call); @@ -302,13 +295,13 @@ make_scope :: (parent: *Interp_Scope) -> *Interp_Scope { return scope; } -make_interp_value :: (i: *Interp, kind: Interp_Value.Kind) -> *Interp_Value { - value := mem.request_memory(Interp_Value,, allocator = i.allocator); +make_interp_value :: (i: *Vm, kind: Interp_Value.Kind) -> *Interp_Value { + value := mem.request_memory(Interp_Value,, allocator = i.interp_allocator); value.kind = kind; return value; } -try_lazy_init :: (i: *Interp) { +try_lazy_init :: (i: *Vm) { value_nil = make_interp_value(i, .nil); value_true = make_interp_value(i, .bool); diff --git a/x/vm/module.jai b/x/vm/module.jai new file mode 100644 index 0000000..23a6aa3 --- /dev/null +++ b/x/vm/module.jai @@ -0,0 +1,202 @@ +// #module_parameters(RUN_TESTS := false); + +Vm :: struct { + reports: [..]Report; + + parser_allocator: Allocator; + toplevel: [..]*Node; + previous: Token; + filename: string = "(unnamed file)"; + source: string; + offset: int; + + interp_allocator: Allocator; + global: *Interp_Scope; +} + +#load "parser.jai"; +#load "resolver.jai"; +#load "interp.jai"; + +#scope_module; + +Report :: struct { + message: string; + offset: int = -1; + error: bool; + extras: Extra = .show_entire_line; + + Extra :: enum_flags { + hide_location; + show_entire_line; + } +} + +report :: (vm: *Vm, message := "", args: ..Any) -> *Report { + r: *Report; + + if vm.reports.count != 0 { + last := *vm.reports[vm.reports.count - 1]; + if last.offset == vm.offset { + r = last; + } + else { + r = array.append(*vm.reports); + } + } + else { + r = array.append(*vm.reports); + } + + r.message = basic.tprint(message, ..args); + r.offset = vm.offset; + return r; +} + +an_error_was_reported :: (vm: Vm) -> bool { + if vm.reports.count == 0 return false; + + reported_error := false; + for < vm.reports { + if it.error reported_error = true; + + line := -1; + column := -1; + + if !(it.extras & .hide_location) { + for 0..vm.source.count - 1 { + if vm.source[it] == "\n" { + line += 1; + column = 1; + } + else { + column += 1; + } + } + + basic.print("%:%,% ", vm.filename, line, column); + } + + basic.print("%\n", it.message); + + if it.extras & .show_entire_line { + i := it.offset; + start := i; + while i >= 0 { + if vm.source[i] == "\n" { + start = i + 1; + break; + } + + i -= 1; + } + + end := it.offset; + + i = it.offset; + while i < vm.source.count { + if vm.source[i] == "\n" { + end = i; + break; + } + + i += 1; + } + + line := string.{ data = vm.source.data + start, count = end - start }; + basic.print("\t%\n", strings.trim(line)); + } + } + + return reported_error; +} + +// exported to the entire module since we want these everywhere + +mem :: #import "jc/memory"; +array :: #import "jc/array"; +kv :: #import "jc/kv"; + +basic :: #import "Basic"; // @future +strings :: #import "String"; // @future + +#scope_file; + +#run { + vm: Vm; + + ok := parse_string(*vm, #string END + fn add(l, r) do return l + r end + fn sub(l, r) do return l - r end + fn mul(l, r) do return l * r end + fn div(l, r) do return l / r end + + var x = 21.0 + var y = 22.0 + var z = x + y + + x = x + 1.0 / 2.0 + print x + + x = add(x, div(1.0, 2.0)) + print x + + print x == x + print x == y + print x == z + + assert x == y + assert x != z + + // def ( + // Add = poly[T] proc(x T, y T) T do return x + y end + // Sub = poly[T] proc(x T, y T) T do return x - y end + // Mul = poly[T] proc(x T, y T) T do return x * y end + // Div = poly[T] proc(x T, y T) T do return x / y end + // ) + + // def ( + // Addi = Add[int] + // Addf = Add[float] + // Subi = Sub[int] + // Subf = Sub[float] + // Muli = Mul[int] + // Mulf = Mul[float] + // Divi = Div[int] + // Divf = Div[float] + // ) + + // def Foo = struct { + // x int = 1 + // y int = 2 + // z int = 3 + // } + + // def Value = union { + // i int + // f float + // b bool + // } + + // def Kind = enum { + // a + // b + // c + // d + // } + + // var foo = Foo{ x = 10, y = 20, z = 30 } + // var val = Value{ f = 3.14 } + // var kind = Kind.a + END); + + if an_error_was_reported(vm) return; + + resolve_everything(*vm); + // generate_bytecode(*vm); + // interp_bytecode(*vm); +} + +// #if RUN_TESTS { +// test :: #import "jc/test"; +// } diff --git a/vm/parser.jai b/x/vm/parser.jai similarity index 91% rename from vm/parser.jai rename to x/vm/parser.jai index 30ad998..66562e9 100644 --- a/vm/parser.jai +++ b/x/vm/parser.jai @@ -296,17 +296,7 @@ Node_Return :: struct { } } -Parser :: struct { - allocator: Allocator; - toplevel: [..]*Node; - - previous: Token; - filename: string; - source: string; - offset: int; -} - -parse_string :: (p: *Parser, source: string) -> bool { +parse_string :: (p: *Vm, source: string) -> bool { try_lazy_init(p); p.source = source; @@ -319,7 +309,7 @@ parse_string :: (p: *Parser, source: string) -> bool { } node := parse_statement(p); - if node != null array.append(*p.toplevel, node); + if node == null array.append(*p.toplevel, node); } return false; @@ -328,7 +318,7 @@ parse_string :: (p: *Parser, source: string) -> bool { #scope_file; -parse_statement :: (p: *Parser) -> *Node { +parse_statement :: (p: *Vm) -> *Node { t := peek_token(p); if t.kind == { // var sym type_expr @@ -470,9 +460,10 @@ parse_statement :: (p: *Parser) -> *Node { return parse_simple_statement(p); } -parse_simple_statement :: (p: *Parser) -> *Node { +parse_simple_statement :: (p: *Vm) -> *Node { dst := parse_expression(p); - basic.assert(dst != null, "expected expression for simple statement"); + if dst == null return null; + // basic.assert(dst != null, "expected expression for simple statement"); t := peek_token(p); if t.kind == { @@ -519,7 +510,7 @@ parse_simple_statement :: (p: *Parser) -> *Node { return dst; } -parse_block :: (p: *Parser) -> *Node_Block { +parse_block :: (p: *Vm) -> *Node_Block { t, ok := expect_token(p, .kw_do); basic.assert(ok, "expected 'do' found '%'", t.str); // @errors @@ -541,7 +532,7 @@ parse_block :: (p: *Parser) -> *Node_Block { return block; } -parse_type_expression :: (p: *Parser) -> *Node_Type { +parse_type_expression :: (p: *Vm) -> *Node_Type { t, ok := expect_token(p, .symbol, .star, .l_square); basic.assert(ok, "type expression"); // @errors @@ -594,7 +585,7 @@ parse_type_expression :: (p: *Parser) -> *Node_Type { return null; } -parse_expression :: (p: *Parser, min_precedence := 1) -> *Node { +parse_expression :: (p: *Vm, min_precedence := 1) -> *Node { get_precedence :: inline (t: Token) -> int { if t.kind == { case .star; #through; @@ -620,7 +611,7 @@ parse_expression :: (p: *Parser, min_precedence := 1) -> *Node { } node := parse_expression_unary(p); - basic.assert(node != null, "expected expression"); // @errors + if node == null return null; while !at_end(p) { op := peek_token(p); @@ -644,7 +635,7 @@ parse_expression :: (p: *Parser, min_precedence := 1) -> *Node { return node; } -parse_expression_unary :: (p: *Parser) -> *Node { +parse_expression_unary :: (p: *Vm) -> *Node { op := peek_token(p); if op.kind == { case .plus; #through; @@ -663,10 +654,10 @@ parse_expression_unary :: (p: *Parser) -> *Node { return parse_expression_postfix(p); } -parse_expression_postfix :: (p: *Parser) -> *Node { +parse_expression_postfix :: (p: *Vm) -> *Node { // @TODO base := parse_expression_base(p); - basic.assert(base != null, "expected expression"); // @errors + if base == null return null; t := peek_token(p); if t.kind == { @@ -717,9 +708,9 @@ parse_expression_postfix :: (p: *Parser) -> *Node { return base; } -parse_expression_base :: (p: *Parser) -> *Node { +parse_expression_base :: (p: *Vm) -> *Node { t, ok := expect_token(p, .kw_true, .kw_false, .number, .symbol, .l_paren); - basic.assert(ok, "expected expression, found '%'", t.str); // @errors + if !ok return null; if t.kind == { case .kw_true; #through; @@ -742,7 +733,10 @@ parse_expression_base :: (p: *Parser) -> *Node { node.value_kind = .float; value, ok := strings.parse_float(*copy); - basic.assert(ok, "malformed float '%'", t.str); // @errors + if !ok { + report(p, "malformed float '%'", t.str); // @location + return null; + } node.f = value; } @@ -753,7 +747,10 @@ parse_expression_base :: (p: *Parser) -> *Node { } value, ok := strings.parse_int(*copy); - basic.assert(ok, "malformed integer '%'", t.str); // @errors + if !ok { + report(p, "malformed integer '%'", t.str); // @location + return null; + } node.i = value; } @@ -762,10 +759,16 @@ parse_expression_base :: (p: *Parser) -> *Node { case .l_paren; node := parse_expression(p); - basic.assert(node != null, "expected expression"); // @errors + if node == null { + report(p, "expected an expression after '('"); + return null; + } - _, ok := expect_token(p, .r_paren); - basic.assert(ok, "expected ')'"); // @errors + t, ok := expect_token(p, .r_paren); + if !ok { + report(p, "expected ')', found '%'", t.str); // @errors + return null; + } return node; } @@ -773,29 +776,29 @@ parse_expression_base :: (p: *Parser) -> *Node { return null; } -make_node :: (p: *Parser, $T: Type) -> *T { - node := mem.request_memory(T,, allocator = p.allocator); +make_node :: (p: *Vm, $T: Type) -> *T { + node := mem.request_memory(T,, allocator = p.parser_allocator); #if #exists(T.init) { - T.init(node, p.allocator); + T.init(node, p.parser_allocator); } return node; } -make_node :: (p: *Parser, $type_kind: Node_Type.Type_Kind) -> *Node_Type { - type := mem.request_memory(Node_Type,, allocator = p.allocator); +make_node :: (p: *Vm, $type_kind: Node_Type.Type_Kind) -> *Node_Type { + type := mem.request_memory(Node_Type,, allocator = p.parser_allocator); type.type_kind = type_kind; - Node_Type.init(type, p.allocator); + Node_Type.init(type, p.parser_allocator); return type; } -peek_token :: (p: *Parser) -> Token { +peek_token :: (p: *Vm) -> Token { copy := p.*; return consume_token(*copy); } -at_end :: (p: *Parser) -> bool { +at_end :: (p: *Vm) -> bool { return p.offset >= p.source.count; } @@ -815,8 +818,10 @@ continues_number :: (c: u8) -> bool { return starts_number(c) || c == "."; } -consume_token :: (p: *Parser) -> Token { - if at_end(p) return .{ kind = .end_of_file }; +consume_token :: (p: *Vm) -> Token { + if at_end(p) { + return .{ kind = .end_of_file }; + } c := p.source[p.offset]; @@ -947,7 +952,7 @@ consume_token :: (p: *Parser) -> Token { return .{ kind = .invalid, str = s }; } -expect_token :: (p: *Parser, kinds: ..Token.Kind) -> Token, bool { +expect_token :: (p: *Vm, kinds: ..Token.Kind) -> Token, bool { t := consume_token(p); for kinds if it == t.kind { return t, true; @@ -956,7 +961,7 @@ expect_token :: (p: *Parser, kinds: ..Token.Kind) -> Token, bool { return t, false; } -try_lazy_init :: (p: *Parser) { - mem.lazy_set_allocator(p); - mem.lazy_set_allocator(*p.toplevel); +try_lazy_init :: (p: *Vm) { + mem.lazy_set_allocator(*p.toplevel, p.parser_allocator); + mem.lazy_set_allocator(*p.reports, p.parser_allocator); } diff --git a/x/vm/resolver.jai b/x/vm/resolver.jai new file mode 100644 index 0000000..fecb2fe --- /dev/null +++ b/x/vm/resolver.jai @@ -0,0 +1,2 @@ +resolve_everything :: (vm: *Vm) { +}