diff --git a/lib/bait/ast/ast.bt b/lib/bait/ast/ast.bt index 7131a2a6..c854bece 100644 --- a/lib/bait/ast/ast.bt +++ b/lib/bait/ast/ast.bt @@ -5,9 +5,9 @@ package ast import bait.token import bait.errors -type Stmt := AssertStmt | AssignStmt | ConstDecl | EnumDecl | ExprStmt | ForLoop | ForClassicLoop | ForInLoop | FunDecl | GlobalDecl | InterfaceDecl | ReturnStmt | StructDecl | TypeDecl | LoopControlStmt | EmptyStmt | IfMatch +type Stmt := AssertStmt | AssignStmt | ConstDecl | EnumDecl | ExprStmt | ForLoop | ForClassicLoop | ForInLoop | FunDecl | GlobalDecl | InterfaceDecl | ReturnStmt | StructDecl | TypeDecl | LoopControlStmt | IfMatch | InvalidStmt -type Expr := AnonFun | ArrayInit | AsCast | BoolLiteral | CallExpr | CharLiteral | ComptimeVar | EnumVal | FloatLiteral | HashExpr | Ident | IfMatch | IndexExpr | InfixExpr | IntegerLiteral | MapInit | ParExpr | PrefixExpr | SelectorExpr | StringLiteral | StringInterLiteral | StructInit | TypeOf | Void | EmptyExpr +type Expr := AnonFun | ArrayInit | AsCast | BoolLiteral | CallExpr | CharLiteral | ComptimeVar | EnumVal | FloatLiteral | HashExpr | Ident | IfMatch | IndexExpr | InfixExpr | IntegerLiteral | MapInit | ParExpr | PrefixExpr | SelectorExpr | StringLiteral | StringInterLiteral | StructInit | TypeOf | Void | InvalidExpr pub struct AssertStmt { pub expr Expr @@ -25,7 +25,9 @@ pub struct AssignStmt { pub struct ConstDecl { pub name string - pub expr Expr + pub pkg string + pub reg_name string + pub expr Expr := InvalidExpr{} pub pos token.Pos pub lang Language global typ Type @@ -113,6 +115,7 @@ pub struct LoopControlStmt { pub struct GlobalDecl { global typ Type pub name string + pub pkg string pub expr Expr pub pos token.Pos } @@ -144,7 +147,7 @@ pub struct StructDecl { pub struct StructField { pub name string pub typ Type - pub expr Expr := EmptyExpr{} + pub expr Expr := InvalidExpr{} pub pos token.Pos pub is_mut bool pub is_pub bool @@ -164,8 +167,8 @@ pub struct ArrayInit { global typ Type global elem_type Type pub exprs []Expr - pub length_expr Expr := EmptyExpr{} - pub cap_expr Expr := EmptyExpr{} + pub length_expr Expr := InvalidExpr{} + pub cap_expr Expr := InvalidExpr{} pub pos token.Pos } @@ -186,7 +189,7 @@ pub struct CallExpr { global name string global return_type Type global left_type Type - global left Expr := EmptyExpr{} + global left Expr := InvalidExpr{} global concrete_types []Type global is_field bool global noreturn bool @@ -352,15 +355,11 @@ pub struct TypeOf { pub struct Void {} -pub struct EmptyExpr { +pub struct InvalidExpr { pub pos token.Pos } -pub fun empty_expr() Expr { - return EmptyExpr{} -} - -pub struct EmptyStmt { +pub struct InvalidStmt { pub pos token.Pos } diff --git a/lib/bait/ast/repr.bt b/lib/bait/ast/repr.bt index a589cf05..73de85d6 100644 --- a/lib/bait/ast/repr.bt +++ b/lib/bait/ast/repr.bt @@ -28,7 +28,7 @@ pub fun (e Expr) repr() string { StructInit { e.name + '{..}'} // TODO fields TypeOf { 'typeof ' + e.expr.repr() } Void { 'void' } - EmptyExpr { 'EmptyExpr' } + InvalidExpr { 'InvalidExpr' } } } diff --git a/lib/bait/ast/scope.bt b/lib/bait/ast/scope.bt index 555fc9bc..65800d73 100644 --- a/lib/bait/ast/scope.bt +++ b/lib/bait/ast/scope.bt @@ -3,7 +3,7 @@ package ast pub struct Scope { - pub parent Scope + pub parent &Scope pub objects map[string]ScopeObject } @@ -13,7 +13,7 @@ pub struct ScopeObject{ pub mut is_pub bool pub mut is_mut bool pub mut pkg string - pub mut expr Expr := EmptyExpr{} // Only for constants. Allows resolving their type early + pub mut expr Expr := InvalidExpr{} // Only for constants. Allows resolving their type early } pub enum ObjectKind { @@ -26,6 +26,24 @@ pub enum ObjectKind { label } +pub fun (mut t Table) preregister_scopes(pkg_name string) &Scope { + builtin_scope := t.get_pkg_scope("builtin", 0 as any) + pkg_scope := t.get_pkg_scope(pkg_name, builtin_scope) + return pkg_scope +} + +pub fun (mut t Table) get_pkg_scope(pkg string, parent &Scope) &Scope { + if t.scopes.contains(pkg) { + return t.scopes[pkg] + } + + s := &Scope{ + parent = parent + } + t.scopes[pkg] = s + return s +} + pub fun (s Scope) register(name string, obj ScopeObject) { if s.objects.contains(name) { return diff --git a/lib/bait/ast/table.bt b/lib/bait/ast/table.bt index ce7448e4..9243b456 100644 --- a/lib/bait/ast/table.bt +++ b/lib/bait/ast/table.bt @@ -3,7 +3,9 @@ package ast pub struct Table { - pub mut global_scope Scope + pub mut global_scope &Scope // TODO SCOPES entirely replace by scopes + pub mut ffi_scope &Scope // Contains the C or JS declarations depending on the backend + pub mut scopes map[string]&Scope pub mut fun_decls map[string]FunDecl pub mut type_idxs map[string]Type pub mut type_symbols []TypeSymbol @@ -18,6 +20,9 @@ pub fun new_table() Table { global_scope = Scope{ parent = 0 as any } + ffi_scope = Scope{ + parent = 0 as any + } } t.register_builtins() return t diff --git a/lib/bait/checker/attribute.bt b/lib/bait/checker/attribute.bt index 198c35d0..7d2f713a 100644 --- a/lib/bait/checker/attribute.bt +++ b/lib/bait/checker/attribute.bt @@ -139,7 +139,7 @@ fun (c Checker) check_struct_field_attrs(node ast.StructDecl){ continue } - if attr.name == 'required' and not (field.expr is ast.EmptyExpr) { + if attr.name == 'required' and not (field.expr is ast.InvalidExpr) { c.error('@required on field with default value is redundant', attr.pos) } } diff --git a/lib/bait/checker/checker.bt b/lib/bait/checker/checker.bt index 70915460..d795eade 100644 --- a/lib/bait/checker/checker.bt +++ b/lib/bait/checker/checker.bt @@ -86,7 +86,6 @@ fun (mut c Checker) change_file(file ast.File) { c.pkg = file.pkg_name c.scope = ast.Scope{ - // TODO how to handle recursive sturct inits? parent = c.table.global_scope } } diff --git a/lib/bait/checker/comptime.bt b/lib/bait/checker/comptime.bt index e07fa5c9..79dbece6 100644 --- a/lib/bait/checker/comptime.bt +++ b/lib/bait/checker/comptime.bt @@ -28,7 +28,7 @@ fun (mut c Checker) comptime_if_condition(node ast.Expr) bool { } } } else { - c.error('invalid $if condition', (node as ast.EmptyExpr).pos) + c.error('invalid $if condition', (node as ast.InvalidExpr).pos) } return false } diff --git a/lib/bait/checker/expr.bt b/lib/bait/checker/expr.bt index ff433406..0788717e 100644 --- a/lib/bait/checker/expr.bt +++ b/lib/bait/checker/expr.bt @@ -32,7 +32,7 @@ fun (mut c Checker) expr(mut expr ast.Expr) ast.Type { ast.StructInit { c.struct_init(mut expr) } ast.TypeOf { c.type_of(mut expr) } ast.Void { ast.VOID_TYPE } - ast.EmptyExpr { panic('unexpected EmptyExpr at ${expr.pos}') } + ast.InvalidExpr { panic('unexpected InvalidExpr at ${expr.pos}') } } c.expected_type = expected_save return t @@ -52,13 +52,13 @@ fun (mut c Checker) array_init(mut node ast.ArrayInit) ast.Type { } // Validate fields if present - if not (node.length_expr is ast.EmptyExpr) { + if not (node.length_expr is ast.InvalidExpr) { typ := c.expr(node.length_expr) if not c.check_types(typ, ast.I32_TYPE) { c.error('expected i32, got ${c.table.type_name(typ)}', node.pos) } } - if not (node.cap_expr is ast.EmptyExpr) { + if not (node.cap_expr is ast.InvalidExpr) { typ := c.expr(node.cap_expr) if not c.check_types(typ, ast.I32_TYPE) { c.error('expected i32, got ${c.table.type_name(typ)}', node.pos) @@ -76,7 +76,7 @@ fun (mut c Checker) array_init(mut node ast.ArrayInit) ast.Type { c.expected_type = typ } if not c.check_types(typ, node.elem_type) { - pos := (e as ast.EmptyExpr).pos + pos := (e as ast.InvalidExpr).pos c.error('expected element type ${c.table.type_name(node.elem_type)}, got ${c.table.type_name(typ)}', pos) } } @@ -149,6 +149,7 @@ fun (mut c Checker) hash_expr(node ast.HashExpr) ast.Type { return ast.VOID_TYPE } +// TODO SCOPES cleanup and add comments fun (mut c Checker) ident(mut node ast.Ident) ast.Type { mut obj := c.scope.get(node.name) @@ -156,6 +157,24 @@ fun (mut c Checker) ident(mut node ast.Ident) ast.Type { c.error('const ${node.name} is private', node.pos) } + if obj.typ == ast.PLACEHOLDER_TYPE { + obj = c.table.scopes[node.pkg].get(node.name) + } + + if obj.kind == .constant or obj.kind == .global_ { + if not node.name.contains('.') { + if obj.pkg == "" { + node.name = node.pkg + '.' + node.name + } else { + node.name = obj.pkg + '.' + node.name + } + } + } + + if obj.typ == ast.PLACEHOLDER_TYPE and node.lang != .bait { + obj = c.table.ffi_scope.get(node.name) + } + if obj.typ != ast.PLACEHOLDER_TYPE { node.is_mut = obj.is_mut return obj.typ @@ -231,11 +250,11 @@ fun (mut c Checker) map_init(mut node ast.MapInit) ast.Type { node.val_type = val_type } else { if not c.check_types(key_type, node.key_type) { - key_expr := key as ast.EmptyExpr + key_expr := key as ast.InvalidExpr c.error('expected key type ${c.table.type_name(node.key_type)}, got ${c.table.type_name(key_type)}', key_expr.pos) } if not c.check_types(val_type, node.val_type) { - val_expr := node.vals[i] as ast.EmptyExpr + val_expr := node.vals[i] as ast.InvalidExpr c.error('expected value type ${c.table.type_name(node.val_type)}, got ${c.table.type_name(val_type)}', val_expr.pos) } } diff --git a/lib/bait/checker/redefinition.bt b/lib/bait/checker/redefinition.bt index 649b5c09..7af79e25 100644 --- a/lib/bait/checker/redefinition.bt +++ b/lib/bait/checker/redefinition.bt @@ -50,7 +50,7 @@ fun (mut c Checker) check_redefinitions(redefs_list []string, kind RedefKind, fi if stmt_is_redef(stmt, name, kind) { redefinitions.push(Redefinition{ path = file.path - pos = (stmt as ast.EmptyStmt).pos + pos = (stmt as ast.InvalidStmt).pos name = name signature = c.stmt_signature(stmt) }) diff --git a/lib/bait/checker/stmt.bt b/lib/bait/checker/stmt.bt index 2a83f84b..86b82af2 100644 --- a/lib/bait/checker/stmt.bt +++ b/lib/bait/checker/stmt.bt @@ -30,7 +30,7 @@ fun (mut c Checker) stmt(mut stmt ast.Stmt){ ast.StructDecl { c.struct_decl(stmt) } ast.TypeDecl { c.type_decl(stmt) } else { - s := stmt as ast.EmptyStmt + s := stmt as ast.InvalidStmt c.error('unexpected stmt: ${s}', s.pos) } } @@ -51,7 +51,7 @@ fun (mut c Checker) const_decl(mut node ast.ConstDecl) { } node.typ = c.expr(node.expr) - c.table.global_scope.update_type(node.name, node.typ) + c.table.global_scope.update_type(node.reg_name, node.typ) } fun (mut c Checker) expr_stmt(mut node ast.ExprStmt) { @@ -62,7 +62,7 @@ fun (mut c Checker) expr_stmt(mut node ast.ExprStmt) { if c.is_if_match_expr or c.is_or_block or expr is ast.CallExpr or expr is ast.IfMatch or expr is ast.HashExpr { return } - e := expr as ast.EmptyExpr + e := expr as ast.InvalidExpr c.error('expression evaluated but not used', e.pos) } @@ -85,7 +85,7 @@ fun (c Checker) enum_decl(node ast.EnumDecl) { } } - if field.expr is ast.EmptyExpr { + if field.expr is ast.InvalidExpr { field.expr = ast.IntegerLiteral{ val = '${cur_val}' } cur_val += 1 continue @@ -181,7 +181,7 @@ fun (mut c Checker) for_in_loop(mut node ast.ForInLoop) { fun (mut c Checker) global_decl(mut node ast.GlobalDecl){ node.typ = c.expr(node.expr) - c.table.global_scope.update_type(node.name, node.typ) + c.table.scopes[node.pkg].update_type(node.name, node.typ) } fun (c Checker) interface_decl(node ast.InterfaceDecl){ diff --git a/lib/bait/checker/struct.bt b/lib/bait/checker/struct.bt index 8f2d91cf..961dff9d 100644 --- a/lib/bait/checker/struct.bt +++ b/lib/bait/checker/struct.bt @@ -37,12 +37,12 @@ fun (mut c Checker) struct_decl(node ast.StructDecl) { } // Check the default value if present - if field.expr is ast.EmptyExpr { + if field.expr is ast.InvalidExpr { continue } typ := c.expr(field.expr) if not c.check_types(typ, field.typ) { - c.error('default value not matches field type ${c.table.type_name(field.typ)}', (field.expr as ast.EmptyExpr).pos) + c.error('default value not matches field type ${c.table.type_name(field.typ)}', (field.expr as ast.InvalidExpr).pos) } } @@ -108,7 +108,7 @@ fun (c Checker) check_init_field_values(init ast.StructInit, info ast.StructInfo continue } - if not (def_field.expr is ast.EmptyExpr) { + if not (def_field.expr is ast.InvalidExpr) { continue } diff --git a/lib/bait/gen/c/assert.bt b/lib/bait/gen/c/assert.bt index bdbc543a..16244226 100644 --- a/lib/bait/gen/c/assert.bt +++ b/lib/bait/gen/c/assert.bt @@ -18,11 +18,11 @@ fun (mut g Gen) assert_stmt(node ast.AssertStmt) { g.write('if (') g.expr(node.expr) g.writeln(') {') - g.writeln('\tTestRunner_assert_pass(&test_runner);') + g.writeln('\tTestRunner_assert_pass(&builtin__test_runner);') g.writeln('} else {') if node.expr is ast.InfixExpr { expr := node.expr as ast.InfixExpr - g.write('\tTestRunner_set_assert_info(&test_runner, ${node.pos.line}, from_c_string("assert ') + g.write('\tTestRunner_set_assert_info(&builtin__test_runner, ${node.pos.line}, from_c_string("assert ') g.assert_side_expr(expr.left) g.write(' ${expr.op.js_repr()} ') g.assert_side_expr(expr.right) @@ -31,12 +31,12 @@ fun (mut g Gen) assert_stmt(node ast.AssertStmt) { g.write(', ') g.expr_to_string(expr.right, expr.right_type) g.writeln(');') - g.writeln('\tTestRunner_assert_fail_infix(&test_runner);') + g.writeln('\tTestRunner_assert_fail_infix(&builtin__test_runner);') } else { - g.write('\tTestRunner_set_assert_info(&test_runner, ${node.pos.line}, from_c_string("assert ') + g.write('\tTestRunner_set_assert_info(&builtin__test_runner, ${node.pos.line}, from_c_string("assert ') g.assert_side_expr(node.expr) g.writeln('"), from_c_string(""), from_c_string(""));') - g.writeln('\tTestRunner_assert_fail(&test_runner);') + g.writeln('\tTestRunner_assert_fail(&builtin__test_runner);') } g.writeln('}') } diff --git a/lib/bait/gen/c/auto_str.bt b/lib/bait/gen/c/auto_str.bt index 63dbcd88..8b0c6a5e 100644 --- a/lib/bait/gen/c/auto_str.bt +++ b/lib/bait/gen/c/auto_str.bt @@ -11,7 +11,7 @@ const LB := if os.platform() == 'windows' { '\\r\\n' } else { '\\n' } fun (mut g Gen) get_str_fun(typ ast.Type) string { g.table.needed_str_funs.push(typ) sym := g.table.get_sym(typ) - return c_name('${sym.name}_str') + return c_esc('${sym.name}_str') } fun (mut g Gen) generate_str_fun(typ ast.Type) { @@ -26,7 +26,7 @@ fun (mut g Gen) generate_str_fun(typ ast.Type) { g.generated_str_funs.push(typ) - sname := c_name(sym.name) + sname := c_esc(sym.name) fname := sname + '_str' if sym.kind == .struct_ { diff --git a/lib/bait/gen/c/cgen.bt b/lib/bait/gen/c/cgen.bt index 7b5eae1c..65852bf5 100644 --- a/lib/bait/gen/c/cgen.bt +++ b/lib/bait/gen/c/cgen.bt @@ -109,16 +109,16 @@ fun (mut g Gen) gen_test_main() { for key, func in g.table.fun_decls { if func.is_test { nr_test_funs += 1 - name := c_name(func.name) + name := c_esc(func.name) esc_path := g.path.replace('\\', '\\\\') - g.writeln('TestRunner_set_test_info(&test_runner, from_c_string("${esc_path}"), from_c_string("${name}"));') + g.writeln('TestRunner_set_test_info(&builtin__test_runner, from_c_string("${esc_path}"), from_c_string("${name}"));') g.writeln('${name}();') } } if g.has_test_end { g.writeln('testsuite_end();') } - g.writeln('exit(TestRunner_exit_code(test_runner));') + g.writeln('exit(TestRunner_exit_code(builtin__test_runner));') g.indent -= 2 g.writeln('}\n') @@ -142,7 +142,7 @@ fun (g Gen) get_concrete_name(name string, concrete_types []ast.Type) string { for t in concrete_types { full_name += '_' + g.table.get_sym(t).name } - return c_name(full_name) + return c_esc(full_name) } fun (mut g Gen) new_temp_var() string { @@ -202,8 +202,14 @@ fun (mut g Gen) save_stmt_offset() { g.stmt_offsets.push(g.out.length) } +// TODO evaluate where c_esc can be replaced by this (see also jsgen) +// TODO consider splitting this up even further for different places fun c_name(n string) string { - name := n.replace('.', '__').replace('[]', 'Array_') + return n.replace('.', '__').replace('[]', 'Array_') +} + +fun c_esc(n string) string { + name := c_name(n) if C_RESERVED.contains(name) { return 'bait_${name}' } diff --git a/lib/bait/gen/c/expr.bt b/lib/bait/gen/c/expr.bt index 03e61f0d..7c4ac81c 100644 --- a/lib/bait/gen/c/expr.bt +++ b/lib/bait/gen/c/expr.bt @@ -32,7 +32,7 @@ fun (mut g Gen) expr(expr ast.Expr) { ast.StructInit { g.struct_init(expr) } ast.TypeOf{ g.type_of(expr)} ast.Void {} // nothing to do - ast.EmptyExpr { panic('unexpected EmptyExpr') } + ast.InvalidExpr { panic('unexpected InvalidExpr') } } } @@ -51,13 +51,13 @@ fun (mut g Gen) array_init(node ast.ArrayInit){ if node.exprs.length == 0 { g.write('new_array(') - if node.length_expr is ast.EmptyExpr { + if node.length_expr is ast.InvalidExpr { g.write('0, ') } else { g.expr(node.length_expr) g.write(', ') } - if node.cap_expr is ast.EmptyExpr { + if node.cap_expr is ast.InvalidExpr { g.write('0, ') } else { g.expr(node.cap_expr) @@ -100,7 +100,7 @@ fun (mut g Gen) char_literal(node ast.CharLiteral) { } fun (mut g Gen) enum_val(node ast.EnumVal) { - g.write(c_name(node.val)) + g.write(c_esc(node.val)) } fun (mut g Gen) hash_expr(node ast.HashExpr) { @@ -109,7 +109,7 @@ fun (mut g Gen) hash_expr(node ast.HashExpr) { fun (mut g Gen) ident(node ast.Ident) { if node.lang == .bait { - g.write(c_name(node.name)) + g.write(c_esc(node.name)) return } g.write(node.name.replace('C.', '')) @@ -180,7 +180,7 @@ fun (mut g Gen) infix_expr(node ast.InfixExpr) { if node.op == .ne { g.write('!') } - g.write(c_name(lsym.name + '_' + overload.name)) + g.write(c_esc(lsym.name + '_' + overload.name)) g.write('(') g.expr(node.left) g.write(', ') @@ -255,7 +255,7 @@ fun (mut g Gen) struct_init(node ast.StructInit) { info := g.table.get_sym(node.typ).info as ast.StructInfo for i, field in info.fields { - name := c_name(field.name) + name := c_esc(field.name) g.write('.${name} = ') init_idx := inited_fields.index(field.name) if init_idx == -1 { @@ -291,7 +291,7 @@ fun (mut g Gen) expr_to_string(expr ast.Expr, typ ast.Type) { str_def := g.table.get_method(sym, 'str') if str_def.name.length > 0 { final_sym := g.table.get_sym(str_def.params[0].typ) - mut name := c_name(final_sym.name) + mut name := c_esc(final_sym.name) g.write('${name}_str(') g.expr(expr) g.write(')') diff --git a/lib/bait/gen/c/fun.bt b/lib/bait/gen/c/fun.bt index a28a1231..29e579d3 100644 --- a/lib/bait/gen/c/fun.bt +++ b/lib/bait/gen/c/fun.bt @@ -47,10 +47,10 @@ fun (mut g Gen) fun_decl(node ast.FunDecl) { g.cur_fun = node type_str := g.typ(node.return_type) - mut name := c_name(node.name) + mut name := c_esc(node.name) if node.is_method { sym := g.table.get_sym(node.params[0].typ) - name = c_name(sym.name + '_' + node.name) + name = c_esc(sym.name + '_' + node.name) } if g.cur_concrete_types.length > 0 { name = g.get_concrete_name(name, g.cur_concrete_types.values()) @@ -68,7 +68,7 @@ fun (mut g Gen) fun_decl(node ast.FunDecl) { fun (mut g Gen) fun_params(params []ast.Param){ for i, p in params { - s := g.typ(p.typ) + ' ' + c_name(p.name) + s := g.typ(p.typ) + ' ' + c_esc(p.name) g.fun_decls_out += s g.write(s) @@ -94,7 +94,7 @@ fun (mut g Gen) call_expr(node ast.CallExpr) { return } - mut name := c_name(node.name) + mut name := c_esc(node.name) if node.is_method { sym := g.table.get_sym(node.left_type) final_sym := g.table.get_final_sym(sym) @@ -113,7 +113,7 @@ fun (mut g Gen) call_expr(node ast.CallExpr) { } } - name = c_name(sym.name + '_' + node.name) + name = c_esc(sym.name + '_' + node.name) } else if node.lang != .bait{ name = node.name.replace('C.', '') } diff --git a/lib/bait/gen/c/stmt.bt b/lib/bait/gen/c/stmt.bt index 6725c61a..84f25665 100644 --- a/lib/bait/gen/c/stmt.bt +++ b/lib/bait/gen/c/stmt.bt @@ -32,7 +32,7 @@ fun (mut g Gen) stmt(stmt ast.Stmt) { ast.ReturnStmt { g.return_stmt(stmt) } ast.StructDecl {} // see write_types() ast.TypeDecl {} // see write_types() - ast.EmptyStmt { panic('unexpected EmptyStmt') } + ast.InvalidStmt { panic('unexpected InvalidStmt') } } } @@ -53,7 +53,7 @@ fun (mut g Gen) assign_stmt(node ast.AssignStmt) { if lsym.overloads.contains(node.op.c_repr()) { g.write(' = ') overload := lsym.overloads[node.op.c_repr()] - g.write(c_name(lsym.name + '_' + overload.name)) + g.write(c_esc(lsym.name + '_' + overload.name)) g.write('(') g.expr(node.left) g.write(', ') @@ -80,19 +80,22 @@ fun (mut g Gen) assign_stmt(node ast.AssignStmt) { } fun (mut g Gen) const_decl(node ast.ConstDecl){ + // TODO SCOPES remove ffi consts in transformer (also jsgen) if node.lang != .bait { return } + pkg := c_name(node.pkg) name := c_name(node.name) + full_name := pkg + '__' + name val := g.expr_string(node.expr) if node.expr is ast.ArrayInit or node.expr is ast.CallExpr or node.expr is ast.MapInit { typ := g.typ(node.typ) - g.type_defs_out += '${typ} ${name};\n' - g.main_inits_out += '\t${name} = ${val};\n' + g.type_defs_out += '${typ} ${full_name};\n' + g.main_inits_out += '\t${full_name} = ${val};\n' } else { - g.type_impls_out += '#define ${name} ${val}\n' + g.type_impls_out += '#define ${full_name} ${val}\n' } } @@ -163,9 +166,10 @@ fun (mut g Gen) loop_control_stmt(node ast.LoopControlStmt){ fun (mut g Gen) global_decl(node ast.GlobalDecl){ name := c_name(node.name) + pkg := c_name(node.pkg) expr := g.expr_string(node.expr) typ := g.typ(node.typ) - g.globals_out += '${typ} ${name} = ${expr};\n' + g.globals_out += '${typ} ${pkg}__${name} = ${expr};\n' } fun (g Gen) interface_decl(node ast.InterfaceDecl){ @@ -179,7 +183,7 @@ fun (g Gen) interface_decl(node ast.InterfaceDecl){ fun (mut g Gen) return_stmt(node ast.ReturnStmt){ g.save_stmt_offset() g.write('return') - if not (node.expr is ast.EmptyExpr) { + if not (node.expr is ast.InvalidExpr) { g.write(" ") g.expr(node.expr) } diff --git a/lib/bait/gen/c/type.bt b/lib/bait/gen/c/type.bt index ffb606ee..f455d47b 100644 --- a/lib/bait/gen/c/type.bt +++ b/lib/bait/gen/c/type.bt @@ -28,14 +28,14 @@ fun (mut g Gen) write_types() { continue } - cname := c_name(sym.name) + cname := c_esc(sym.name) if sym.info is ast.StructInfo { info := sym.info as ast.StructInfo g.type_defs_out += 'typedef struct ${cname} ${cname};\n' g.type_impls_out += 'struct ${cname} {\n' for field in info.fields { type_str := g.typ(field.typ) - field_name := c_name(field.name) + field_name := c_esc(field.name) g.type_impls_out += '\t${type_str} ${field_name};\n' } g.type_impls_out += '};\n' @@ -62,7 +62,7 @@ fun (mut g Gen) write_types() { info := sym.info as ast.EnumInfo g.type_defs_out += 'enum ${cname} {\n' for val in info.vals { - g.type_defs_out += '\t${c_name(val)},\n' + g.type_defs_out += '\t${c_esc(val)},\n' } g.type_defs_out += '};\n' } diff --git a/lib/bait/gen/js/auto_str.bt b/lib/bait/gen/js/auto_str.bt index 59f8750f..e887ca55 100644 --- a/lib/bait/gen/js/auto_str.bt +++ b/lib/bait/gen/js/auto_str.bt @@ -11,7 +11,7 @@ const LB := if os.platform() == 'windows' { '\\r\\n' } else { '\\n' } fun (mut g Gen) get_str_fun(typ ast.Type) string { g.table.needed_str_funs.push(typ) sym := g.table.get_sym(typ) - return js_name('${sym.name}_str') + return js_esc('${sym.name}_str') } fun (mut g Gen) generate_str_fun(typ ast.Type) { @@ -26,7 +26,7 @@ fun (mut g Gen) generate_str_fun(typ ast.Type) { g.generated_str_funs.push(typ) - name := js_name('${sym.name}_str') + name := js_esc('${sym.name}_str') if sym.kind == .array { info := sym.info as ast.ArrayInfo @@ -103,7 +103,7 @@ fun (mut g Gen) generate_str_fun(typ ast.Type) { g.fun_decls_out += 'function ${name}(it, indent) {\n' for var in info.variants { var_sym := g.table.get_sym(var) - g.fun_decls_out += '\tif (it instanceof ${js_name(var_sym.name)}) { + g.fun_decls_out += '\tif (it instanceof ${js_esc(var_sym.name)}) { return ${g.get_str_fun(var)}(it, indent) } ' @@ -117,7 +117,7 @@ fun (mut g Gen) generate_str_fun(typ ast.Type) { switch(it) {\n' info := sym.info as ast.EnumInfo for val in info.vals { - g.fun_decls_out += '\t\tcase ${js_name(sym.name)}.${val}: return from_js_string("${val}")\n' + g.fun_decls_out += '\t\tcase ${js_esc(sym.name)}.${val}: return from_js_string("${val}")\n' } g.fun_decls_out += '\t}\n}\n\n' return diff --git a/lib/bait/gen/js/expr.bt b/lib/bait/gen/js/expr.bt index fb8b8b25..f4c88ad1 100644 --- a/lib/bait/gen/js/expr.bt +++ b/lib/bait/gen/js/expr.bt @@ -34,7 +34,7 @@ fun (mut g Gen) expr(expr ast.Expr) { ast.StructInit { g.struct_init(expr) } ast.TypeOf{ g.type_of(expr)} ast.Void {} // nothing to do - ast.EmptyExpr { panic('unexpected EmptyExpr') } + ast.InvalidExpr { panic('unexpected InvalidExpr') } } } @@ -63,7 +63,7 @@ fun (mut g Gen) anon_fun(node ast.AnonFun) { fun (mut g Gen) array_init(node ast.ArrayInit) { g.write('new bait_Array({ data: ') - if node.length_expr is ast.EmptyExpr { + if node.length_expr is ast.InvalidExpr { g.write('[') for i, expr in node.exprs { g.expr(expr) @@ -117,7 +117,7 @@ fun (mut g Gen) char_literal(node ast.CharLiteral) { } fun (mut g Gen) enum_val(node ast.EnumVal) { - g.write(js_name(node.name) + '.' + node.val) + g.write(js_esc(node.name) + '.' + node.val) } fun (mut g Gen) float_literal(node ast.FloatLiteral){ @@ -134,7 +134,7 @@ fun (mut g Gen) ident(node ast.Ident) { return } - g.write(js_name(node.name)) + g.write(js_esc(node.name)) } fun (mut g Gen) integer_literal(node ast.IntegerLiteral){ @@ -205,7 +205,7 @@ fun (mut g Gen) infix_expr(node ast.InfixExpr){ if node.op == .ne { g.write('!') } - g.write(js_name(lsym.name + '_' + overload.name)) + g.write(js_esc(lsym.name + '_' + overload.name)) g.write('(') g.expr(node.left) g.write(', ') @@ -324,7 +324,7 @@ fun (mut g Gen) expr_to_string(expr ast.Expr, typ ast.Type) { str_def := g.table.get_method(sym, 'str') if str_def.name.length > 0 { final_sym := g.table.get_sym(str_def.params[0].typ) - mut name := js_name(final_sym.name) + mut name := js_esc(final_sym.name) g.write('${name}_str(') g.expr(expr) g.write(')') diff --git a/lib/bait/gen/js/fun.bt b/lib/bait/gen/js/fun.bt index ea79ab7e..0c95ed81 100644 --- a/lib/bait/gen/js/fun.bt +++ b/lib/bait/gen/js/fun.bt @@ -34,9 +34,9 @@ fun (mut g Gen) fun_decl(node ast.FunDecl) { mut name := '' if node.is_method { sym := g.table.get_sym(node.params[0].typ) - name = js_name(sym.name + '_' + node.name) + name = js_esc(sym.name + '_' + node.name) } else { - name = js_name(node.name) + name = js_esc(node.name) } if g.cur_concrete_types.length > 0 { name = g.get_concrete_name(name, g.cur_concrete_types.values()) @@ -50,7 +50,7 @@ fun (mut g Gen) fun_decl(node ast.FunDecl) { export_attr := node.attrs.find_attr('export') if export_attr.name != '' { - g.writeln('module.exports.${export_attr.value} = ${js_name(node.name)}') + g.writeln('module.exports.${export_attr.value} = ${js_esc(node.name)}') } g.writeln('') @@ -58,7 +58,7 @@ fun (mut g Gen) fun_decl(node ast.FunDecl) { fun (mut g Gen) fun_params(params []ast.Param){ for i, p in params { - g.write(js_name(p.name)) + g.write(js_esc(p.name)) if i < params.length - 1 { g.write(", ") } @@ -142,9 +142,9 @@ fun (mut g Gen) call_expr_no_or(node ast.CallExpr) { return } - name = js_name(sym.name + '_' + node.name) + name = js_esc(sym.name + '_' + node.name) } else if node.lang == .bait{ - name = js_name(node.name) + name = js_esc(node.name) } if node.concrete_types.length > 0 { diff --git a/lib/bait/gen/js/jsgen.bt b/lib/bait/gen/js/jsgen.bt index 33370921..9785b743 100644 --- a/lib/bait/gen/js/jsgen.bt +++ b/lib/bait/gen/js/jsgen.bt @@ -187,16 +187,16 @@ fun (mut g Gen) gen_test_main() { for key, func in g.table.fun_decls { if func.is_test { nr_test_funs += 1 - name := js_name(func.name) + name := js_esc(func.name) esc_path := g.path.replace('\\', '\\\\') - g.writeln('TestRunner_set_test_info(test_runner, from_js_string("${esc_path}"), from_js_string("${name}"))') + g.writeln('TestRunner_set_test_info(builtin__test_runner, from_js_string("${esc_path}"), from_js_string("${name}"))') g.writeln('${name}()') } } if g.has_test_end { g.writeln('testsuite_end()') } - g.writeln('exit(TestRunner_exit_code(test_runner))') + g.writeln('exit(TestRunner_exit_code(builtin__test_runner))') g.indent -= 1 g.writeln('}') @@ -241,7 +241,7 @@ fun (mut g Gen) write_default_value(typ ast.Type) { keys = []ast.Expr }) } else if sym.kind == .struct_ { - g.write('new ${js_name(sym.name)}({})') + g.write('new ${js_esc(sym.name)}({})') } else if sym.kind == .alias_type { g.write_default_value(sym.parent) } else if sym.kind == .enum_ { @@ -259,7 +259,7 @@ fun (g Gen) get_concrete_name(name string, concrete_types []ast.Type) string { for t in concrete_types { full_name += '_' + g.table.get_sym(t).name } - return js_name(full_name) + return js_esc(full_name) } fun (g Gen) concrete_sym(typ ast.Type) ast.TypeSymbol { @@ -271,8 +271,14 @@ fun (g Gen) concrete_sym(typ ast.Type) ast.TypeSymbol { return g.table.get_sym(g.cur_concrete_types[sym.name]) } +// TODO evaluate where js_esc can be replaced by this (see also cgen) +// TODO consider splitting this up even further for different places fun js_name(n string) string { - name := n.replace('.', '__').replace('[]', 'Array_').replace('[', '_').replace(']', '_') + return n.replace('.', '__').replace('[]', 'Array_').replace('[', '_').replace(']', '_') +} + +fun js_esc(n string) string { + name := js_name(n) // TODO this might have bad performance impact due to array size and frequent use if JS_RESERVED.contains(name) { return 'bait_${name}' diff --git a/lib/bait/gen/js/stmt.bt b/lib/bait/gen/js/stmt.bt index 4532be6e..96b059e8 100644 --- a/lib/bait/gen/js/stmt.bt +++ b/lib/bait/gen/js/stmt.bt @@ -69,11 +69,11 @@ fun (mut g Gen) assert_stmt(node ast.AssertStmt) { g.write('if (') g.expr(node.expr) g.writeln(') {') - g.writeln('\tTestRunner_assert_pass(test_runner)') + g.writeln('\tTestRunner_assert_pass(builtin__test_runner)') g.writeln('} else {') if node.expr is ast.InfixExpr { expr := node.expr as ast.InfixExpr - g.write('\tTestRunner_set_assert_info(test_runner, ${node.pos.line}, from_js_string("assert ') + g.write('\tTestRunner_set_assert_info(builtin__test_runner, ${node.pos.line}, from_js_string("assert ') g.assert_side_expr(expr.left) g.write(' ${expr.op.js_repr()} ') g.assert_side_expr(expr.right) @@ -82,12 +82,12 @@ fun (mut g Gen) assert_stmt(node ast.AssertStmt) { g.write(', ') g.expr_to_string(expr.right, expr.right_type) g.writeln(')') - g.writeln('\tTestRunner_assert_fail_infix(test_runner)') + g.writeln('\tTestRunner_assert_fail_infix(builtin__test_runner)') } else { - g.write('\tTestRunner_set_assert_info(test_runner, ${node.pos.line}, from_js_string("assert ') + g.write('\tTestRunner_set_assert_info(builtin__test_runner, ${node.pos.line}, from_js_string("assert ') g.assert_side_expr(node.expr) g.writeln('"), from_js_string(""), from_js_string(""), from_js_string(""))') - g.writeln('\tTestRunner_assert_fail(test_runner)') + g.writeln('\tTestRunner_assert_fail(builtin__test_runner)') } g.writeln('}') } @@ -160,7 +160,7 @@ fun (mut g Gen) assign_stmt(node ast.AssignStmt){ if lsym.overloads.contains(node.op.js_repr()) { g.write(' = ') overload := lsym.overloads[node.op.js_repr()] - g.write(js_name(lsym.name + '_' + overload.name)) + g.write(js_esc(lsym.name + '_' + overload.name)) g.write('(') g.expr(node.left) g.write(', ') @@ -186,14 +186,16 @@ fun (mut g Gen) assign_stmt(node ast.AssignStmt){ } fun (mut g Gen) const_decl(node ast.ConstDecl){ + // TODO SCOPES remove ffi consts in transformer (also cgen) if node.lang != .bait { return } + pkg := js_name(node.pkg) + name := js_name(node.name) + g.save_stmt_offset() - g.write('const ') - g.write(js_name(node.name)) - g.write(' = ') + g.write('const ${pkg}__${name} = ') g.expr(node.expr) g.writeln('') } @@ -203,7 +205,7 @@ fun (mut g Gen) enum_decl(node ast.EnumDecl){ return } - g.writeln('const ' + js_name(node.name) + ' = {') + g.writeln('const ' + js_esc(node.name) + ' = {') g.indent += 1 for field in node.fields { g.write('${field.name}: ') @@ -258,7 +260,7 @@ fun (mut g Gen) for_classic_loop(node ast.ForClassicLoop){ fun (mut g Gen) for_in_loop(node ast.ForInLoop){ g.write_label(node.label) - mut i := js_name(node.idxvar) + mut i := js_esc(node.idxvar) if i == '' { i = g.new_temp_var() } @@ -269,7 +271,7 @@ fun (mut g Gen) for_in_loop(node ast.ForInLoop){ } container := g.expr_string(node.expr) g.writeln('for (let ${i} = 0; ${i} < ${container}.length; ${i}++) {') - val_name := js_name(node.valvar.name) + val_name := js_esc(node.valvar.name) if sym.kind == .array { g.writeln('\tconst ${val_name} = Array_get(${container}, ${i})') } else if sym.kind == .string { @@ -287,16 +289,17 @@ fun (mut g Gen) for_in_map(node ast.ForInLoop){ g.writeln('const ${keys_var} = Map_keys(${container})') i := g.new_temp_var() g.writeln('for (let ${i} = 0; ${i} < ${keys_var}.length; ${i}++) {') - g.writeln('\tconst ${js_name(node.idxvar)} = Array_get(${keys_var}, ${i})') - g.writeln('\tconst ${js_name(node.valvar.name)} = Map_get(${container}, ${node.idxvar})') + g.writeln('\tconst ${js_esc(node.idxvar)} = Array_get(${keys_var}, ${i})') + g.writeln('\tconst ${js_esc(node.valvar.name)} = Map_get(${container}, ${node.idxvar})') g.stmts(node.stmts) g.writeln('}') } fun (mut g Gen) global_decl(node ast.GlobalDecl){ name := js_name(node.name) + pkg := js_name(node.pkg) expr := g.expr_string(node.expr) - g.global_out += 'var ${name} = ${expr}\n' + g.global_out += 'var ${pkg}__${name} = ${expr}\n' } fun (mut g Gen) interface_decl(node ast.InterfaceDecl){ @@ -311,7 +314,7 @@ fun (mut g Gen) return_stmt(node ast.ReturnStmt){ g.save_stmt_offset() g.write('return') - if not (node.expr is ast.EmptyExpr) { + if not (node.expr is ast.InvalidExpr) { g.write(" ") g.expr(node.expr) } @@ -325,6 +328,6 @@ fun (mut g Gen) type_decl(node ast.TypeDecl){ } g.write('function ') - g.write(js_name(node.name)) + g.write(js_esc(node.name)) g.writeln('(val) { return val }') } diff --git a/lib/bait/gen/js/struct.bt b/lib/bait/gen/js/struct.bt index a7d5d53c..26755384 100644 --- a/lib/bait/gen/js/struct.bt +++ b/lib/bait/gen/js/struct.bt @@ -10,14 +10,14 @@ fun (mut g Gen) struct_decl(node ast.StructDecl){ } g.write('function ') - jsname := js_name(node.pkg_prefix + node.name) + jsname := js_esc(node.pkg_prefix + node.name) g.write(jsname) g.write('({ ') for i, field in node.fields { g.write('${field.name} = ') if node.typ == field.typ { g.write('this') - } else if not (field.expr is ast.EmptyExpr) { + } else if not (field.expr is ast.InvalidExpr) { g.expr(field.expr) } else { g.write_default_value(field.typ) @@ -39,7 +39,7 @@ fun (mut g Gen) struct_decl(node ast.StructDecl){ } fun (mut g Gen) struct_init(node ast.StructInit) { - g.write('new ' + js_name(node.name) + '({') + g.write('new ' + js_esc(node.name) + '({') if node.fields.length == 0 { g.write('})') return diff --git a/lib/bait/parser/expr.bt b/lib/bait/parser/expr.bt index 6310aae9..143830ce 100644 --- a/lib/bait/parser/expr.bt +++ b/lib/bait/parser/expr.bt @@ -78,7 +78,7 @@ fun (mut p Parser) single_expr() !ast.Expr { } .error { p.error(p.val)! - return ast.EmptyExpr{} as ast.Expr + return ast.InvalidExpr{} as ast.Expr } else { mut msg := 'invalid expression: kind = ${p.tok}' @@ -86,7 +86,7 @@ fun (mut p Parser) single_expr() !ast.Expr { msg += ', val = ${p.val}' } p.error(msg)! - return ast.EmptyExpr{} as ast.Expr + return ast.InvalidExpr{} as ast.Expr } } } @@ -114,8 +114,8 @@ fun (mut p Parser) array_init() !ast.ArrayInit{ typ := p.table.find_or_register_array(elem_type) // Optional parameters, e.g. `[]i32{ length = 10 cap = 20 }` - mut length_expr := ast.EmptyExpr{} as ast.Expr - mut cap_expr := ast.EmptyExpr{} as ast.Expr + mut length_expr := ast.InvalidExpr{} as ast.Expr + mut cap_expr := ast.InvalidExpr{} as ast.Expr if p.tok == .lcur { p.next() for p.tok != .rcur { diff --git a/lib/bait/parser/if_match.bt b/lib/bait/parser/if_match.bt index 551c1241..54389265 100644 --- a/lib/bait/parser/if_match.bt +++ b/lib/bait/parser/if_match.bt @@ -27,7 +27,7 @@ fun (mut p Parser) if_match(is_expr bool) !ast.IfMatch { has_else = true stmts := p.parse_block()! branches.push(ast.IfBranch{ - cond = ast.EmptyExpr{} + cond = ast.InvalidExpr{} stmts = stmts pos = bpos }) @@ -86,7 +86,7 @@ fun (mut p Parser) match_as_if_expr(is_expr bool) !ast.IfMatch { has_else = true stmts := p.parse_block()! branches.push(ast.IfBranch{ - cond = ast.EmptyExpr{} + cond = ast.InvalidExpr{} stmts = stmts pos = bpos }) diff --git a/lib/bait/parser/parser.bt b/lib/bait/parser/parser.bt index 1ee37bda..51a4bf28 100644 --- a/lib/bait/parser/parser.bt +++ b/lib/bait/parser/parser.bt @@ -19,6 +19,7 @@ pub struct Parser { mut val string mut pos token.Pos mut pkg_name string + mut pkg_scope &ast.Scope := 0 as any mut import_aliases map[string]string mut attributes []ast.Attribute mut expr_pkg string @@ -82,6 +83,8 @@ pub fun (mut p Parser) parse_result() !ast.File { } } + p.pkg_scope = p.table.preregister_scopes(p.pkg_name) + imports := p.import_stmts()! mut stmts := []ast.Stmt diff --git a/lib/bait/parser/stmt.bt b/lib/bait/parser/stmt.bt index 5fdccad3..9438a06f 100644 --- a/lib/bait/parser/stmt.bt +++ b/lib/bait/parser/stmt.bt @@ -29,7 +29,7 @@ fun (mut p Parser) pub_stmt() !ast.Stmt { .key_struct{ p.struct_decl()! } else{ p.error('cannot use pub keyword before ${p.peek()}')! - ast.EmptyStmt{} + ast.InvalidStmt{} } } } @@ -40,7 +40,7 @@ fun (mut p Parser) script_mode_or_error() !ast.Stmt { } p.error('bad toplevel token: kind = ${p.tok}, val = ${p.val}')! - return ast.EmptyStmt{} + return ast.InvalidStmt{} } fun (mut p Parser) script_mode_main() !ast.FunDecl { @@ -113,23 +113,11 @@ fun (mut p Parser) partial_assign_stmt(left ast.Expr) !ast.AssignStmt { p.next() mut right := p.expr(.lowest)! - // Convert math assign to normal assign - // `x += 1` --> `x = x + 1` - if op.is_math_assign() { - right = ast.InfixExpr{ - op = op.math_from_assign() - left = left - right = right - } - - op = .assign - } - return ast.AssignStmt{ op = op left = left right = right - pos = (left as ast.EmptyExpr).pos + pos = (left as ast.InvalidExpr).pos } } @@ -141,30 +129,43 @@ fun (mut p Parser) const_decl() !ast.ConstDecl{ lang := p.parse_lang()! mut name := '' - // foreign package + // foreign package, e.g. `#JS.process.argv` if lang == .js and p.peek() == .dot { name = p.check_name()! + '.' p.next() } - // normal name and bait package + // actual const name name += p.check_name()! - if lang == .bait { - name = p.prepend_pkg(name) - } p.check(.decl_assign)! - mut expr := ast.empty_expr() - mut typ := ast.PLACEHOLDER_TYPE - if lang == .bait { - expr = p.expr(.lowest)! - typ = p.infer_expr_type(expr) - } else { - typ = p.parse_type()! + if lang != .bait { + typ := p.parse_type()! + + // TODO SCOPES consider not prepending lang. Requires changes to p.hash_expr: do not set expr_pkg and all other FFI constructs + p.table.ffi_scope.register(lang.prepend_to(name), ast.ScopeObject{ + typ = typ + kind = .constant + is_pub = is_pub + pkg = p.pkg_name + }) + + return ast.ConstDecl{ + name = name + typ = typ + pos = pos + lang = lang + } } - p.table.global_scope.register(lang.prepend_to(name), ast.ScopeObject{ + reg_name := p.prepend_pkg(name) + + expr := p.expr(.lowest)! + typ := p.infer_expr_type(expr) + + // p.table.global_scope.register(name, ast.ScopeObject{ + p.table.global_scope.register(reg_name, ast.ScopeObject{ typ = typ kind = .constant is_pub = is_pub @@ -174,6 +175,8 @@ fun (mut p Parser) const_decl() !ast.ConstDecl{ return ast.ConstDecl{ name = name + pkg = p.pkg_name + reg_name = reg_name expr = expr typ = typ pos = pos @@ -218,7 +221,7 @@ fun (mut p Parser) enum_decl() !ast.EnumDecl{ for p.tok != .rcur { fpos := p.pos fname := p.check_name()! - mut expr := ast.EmptyExpr{} as ast.Expr + mut expr := ast.InvalidExpr{} as ast.Expr if p.tok == .decl_assign { p.next() @@ -258,17 +261,18 @@ fun (mut p Parser) enum_decl() !ast.EnumDecl{ fun (mut p Parser) global_decl() !ast.GlobalDecl{ pos := p.pos p.check(.key_global)! - name := p.prepend_pkg(p.check_name()!) + name := p.check_name()! p.check(.decl_assign)! expr := p.expr(.lowest)! typ := p.infer_expr_type(expr) - p.table.global_scope.register(name, ast.ScopeObject{ + p.pkg_scope.register(name, ast.ScopeObject{ is_mut = true typ = typ kind = .global_ }) return ast.GlobalDecl{ name = name + pkg = p.pkg_name expr = expr pos = pos } diff --git a/lib/bait/parser/struct.bt b/lib/bait/parser/struct.bt index 8ca2652b..ba7ebf8c 100644 --- a/lib/bait/parser/struct.bt +++ b/lib/bait/parser/struct.bt @@ -77,7 +77,7 @@ fun (mut p Parser) struct_decl_field(is_mut bool, is_pub bool, is_global bool) ! fname := p.check_name()! ftyp := p.parse_type()! - mut expr := ast.EmptyExpr{} as ast.Expr + mut expr := ast.InvalidExpr{} as ast.Expr if p.tok == .decl_assign { p.next() expr = p.expr(.lowest)!