diff --git a/.github/workflows/compiler_test.yml b/.github/workflows/compiler_test.yml index 9c5d4bf..a4ff8df 100644 --- a/.github/workflows/compiler_test.yml +++ b/.github/workflows/compiler_test.yml @@ -18,5 +18,5 @@ jobs: - name: Run passing tests run: | sudo pip3 install lark - python3 bsc examples/syntax.bs - python3 bsc examples/sema.bs + python3 bsc examples/hello_world.bs + python3 bsc tests/sema.bs diff --git a/bsc/AST.py b/bsc/AST.py index 0ed3398..13fe41a 100644 --- a/bsc/AST.py +++ b/bsc/AST.py @@ -42,7 +42,9 @@ def __init__(self, pkg_name, alias_name, pos): self.sym = None class ModDecl: - def __init__(self, access_modifier, name, is_inline, decls, pos, sym=None): + def __init__( + self, access_modifier, name, is_inline, decls, pos, sym = None + ): self.access_modifier = access_modifier self.name = name self.is_inline = is_inline diff --git a/bsc/__init__.py b/bsc/__init__.py index 609f449..cab955a 100644 --- a/bsc/__init__.py +++ b/bsc/__init__.py @@ -85,24 +85,22 @@ def import_module(self, parent, decl): dir = os.path.dirname(os.path.relpath(decl.pos.file)) file = os.path.join(dir, f"{decl.name}.bs") if os.path.isfile(file): - self.parse_file(decl.name, file, parent_mod=parent) + self.parse_file(decl.name, file, parent_mod = parent) elif os.path.isdir(os.path.join(dir, decl.name)): mod_bs = os.path.join(dir, decl.name, "mod.bs") if os.path.isfile(mod_bs): - self.parse_file(decl.name, file, parent_mod=parent) + self.parse_file(decl.name, file, parent_mod = parent) else: - report.error(f"cannot load module `{decl.name}`, because it does not contain a file `mod.bs`") + report.error( + f"cannot load module `{decl.name}`, because it does not contain a file `mod.bs`" + ) else: report.error(f"module `{decl.name}` not found", decl.pos) def parse_input(self): - self.parse_file( - self.prefs.pkg_name, self.prefs.input, is_pkg = True - ) + self.parse_file(self.prefs.pkg_name, self.prefs.input, is_pkg = True) - def parse_file( - self, mod_name, file, is_pkg = False, parent_mod = None - ): + def parse_file(self, mod_name, file, is_pkg = False, parent_mod = None): self.source_files.append( self.astgen.parse_file(mod_name, file, is_pkg, parent_mod) ) diff --git a/bsc/astgen.py b/bsc/astgen.py index 6a8f629..9148bd3 100644 --- a/bsc/astgen.py +++ b/bsc/astgen.py @@ -22,13 +22,10 @@ def __init__(self, ctx): self.source_file = None self.mod_sym = None - def parse_file( - self, mod_name, file, is_pkg = False, parent_mod = None - ): + def parse_file(self, mod_name, file, is_pkg = False, parent_mod = None): self.file = file self.mod_sym = Module( - AccessModifier.public, mod_name, Scope(self.ctx.universe), - is_pkg + AccessModifier.public, mod_name, Scope(self.ctx.universe), is_pkg ) self.source_file = SourceFile( self.file, self.transform(bs_parser.parse(open(file).read())), @@ -70,10 +67,12 @@ def mod_decl(self, *nodes): if not is_inline: pos += nodes[2].pos mod_sym = Module( - access_modifier, nodes[2].name, Scope(self.mod_sym, True), - False + access_modifier, nodes[2].name, Scope(self.mod_sym, True), False, + is_inline + ) + return ModDecl( + access_modifier, nodes[2].name, is_inline, decls, pos, mod_sym ) - return ModDecl(access_modifier, nodes[2].name, is_inline, decls, pos, mod_sym) def enum_decl(self, *nodes): pos = self.mkpos(nodes[1]) diff --git a/bsc/codegen.py b/bsc/codegen.py index be97986..27e64d3 100644 --- a/bsc/codegen.py +++ b/bsc/codegen.py @@ -32,8 +32,7 @@ def gen_decls(self, decls): def gen_decl(self, decl): if isinstance(decl, AST.ModDecl): - if decl.is_inline: - self.gen_inline_mod(decl) + self.gen_mod(decl) elif isinstance(decl, AST.EnumDecl): self.gen_enum_decl(decl) elif isinstance(decl, AST.FnDecl): @@ -43,18 +42,28 @@ def gen_enum_decl(self, decl): fields = [] for i, f in enumerate(decl.fields): fields.append(LuaTableField(f.name, str(i))) - self.decls.append(LuaTable(decl.sym.mod_qualname("."), fields)) + self.decls.append(LuaTable(decl.sym.codegen_qualname(), fields)) - def gen_inline_mod(self, decl): - old_decls = self.decls - self.decls = [] - self.gen_decls(decl.decls) - old_decls.append(LuaModule(decl.sym.mod_qualname("."), self.decls)) - self.decls = old_decls + def gen_mod(self, decl): + if decl.is_inline: + old_decls = self.decls + self.decls = [] + self.gen_decls(decl.decls) + old_decls.append( + LuaModule(decl.sym.codegen_qualname(), self.decls, True) + ) + self.decls = old_decls + else: + self.decls.append( + LuaModule( + decl.sym.codegen_qualname(), [], False, + lua_filename = decl.name + ) + ) def gen_fn_decl(self, decl): args = [] for arg in decl.args: args.append(LuaIdent(arg.name)) - luafn = LuaFunction(decl.sym.mod_qualname("."), args) + luafn = LuaFunction(decl.sym.codegen_qualname(), args) self.decls.append(luafn) diff --git a/bsc/lua_ast/__init__.py b/bsc/lua_ast/__init__.py index c540247..94966f1 100644 --- a/bsc/lua_ast/__init__.py +++ b/bsc/lua_ast/__init__.py @@ -3,9 +3,11 @@ # LICENSE file. class LuaModule: - def __init__(self, name, decls = []): + def __init__(self, name, decls = [], is_inline = False, lua_filename = ""): self.name = name self.decls = decls + self.is_inline = is_inline + self.lua_filename = lua_filename class LuaTableField: def __init__(self, name, value): diff --git a/bsc/lua_ast/render.py b/bsc/lua_ast/render.py index 5ef5b3c..3a89c81 100644 --- a/bsc/lua_ast/render.py +++ b/bsc/lua_ast/render.py @@ -48,12 +48,22 @@ def render_decls(self, decls): def render_decl(self, decl): if isinstance(decl, LuaModule): - self.render_inline_mod(decl) + self.render_mod(decl) elif isinstance(decl, LuaTable): self.render_table(decl) elif isinstance(decl, LuaFunction): self.render_fn_decl(decl) + def render_mod(self, decl): + if decl.is_inline: + self.writeln(f"{decl.name} = {{}} -- inline module\n") + self.render_decls(decl.decls) + self.writeln(f"-- end module `{decl.name}`\n") + else: + self.writeln( + f"{decl.name} = require(\"bsc-out.{decl.lua_filename}\") -- load module file\n" + ) + def render_table(self, decl): self.writeln(f"{decl.name} = {{") self.indent += 1 @@ -66,10 +76,6 @@ def render_table(self, decl): self.indent -= 1 self.writeln("}\n") - def render_inline_mod(self, decl): - self.writeln(f"{decl.name} = {{}} -- inline module\n") - self.render_decls(decl.decls) - def render_fn_decl(self, decl): self.write(f"function {decl.name}(") for i, arg in enumerate(decl.args): diff --git a/bsc/report.py b/bsc/report.py index a14ac75..0d8cd7e 100644 --- a/bsc/report.py +++ b/bsc/report.py @@ -8,14 +8,15 @@ def _format(pos, kind, kindc, msg): return "{} {}".format( - utils.bold("{}:{}:{}: {}".format(pos.file, pos.line, pos.column, kindc(kind))), - msg + utils.bold( + "{}:{}:{}: {}".format(pos.file, pos.line, pos.column, kindc(kind)) + ), msg ) def error(msg, pos): global errors - utils.eprint(_format(pos, "error: ", utils.red, msg)) + utils.eprint(_format(pos, "error:", utils.red, msg)) errors += 1 def warn(msg, pos): - utils.eprint(_format(pos, "warning: ", utils.yellow, msg)) + utils.eprint(_format(pos, "warning:", utils.yellow, msg)) diff --git a/bsc/sema.py b/bsc/sema.py index aa760ff..24b5a87 100644 --- a/bsc/sema.py +++ b/bsc/sema.py @@ -51,8 +51,10 @@ def check_decl(self, decl): def check_mod_decl(self, decl): old_sym = self.cur_sym - if self.first_pass and decl.is_inline: - self.add_sym(decl.sym, decl.pos) + if self.first_pass: + decl.sym.parent = self.cur_sym + if decl.is_inline: + self.add_sym(decl.sym, decl.pos) self.cur_sym = decl.sym self.cur_scope = decl.sym.scope self.check_decls(decl.decls) diff --git a/bsc/sym.py b/bsc/sym.py index d07cf23..3bbd243 100644 --- a/bsc/sym.py +++ b/bsc/sym.py @@ -56,12 +56,12 @@ def qualname(self, sep = "::"): return f"{self.parent.qualname(sep)}{sep}{self.name}" return self.name - def mod_qualname(self, sep = "::"): + def codegen_qualname(self, sep = "."): if self.parent: - if isinstance(self.parent, Module): + if isinstance(self.parent, Module) and not self.parent.is_inline: return f"{self.parent.name}{sep}{self.name}" if not self.parent.scope.is_universe: - return f"{self.parent.mod_qualname(sep)}{sep}{self.name}" + return f"{self.parent.codegen_qualname(sep)}{sep}{self.name}" return self.name def __repr__(self): @@ -158,11 +158,12 @@ def type_kind(self): return str(self.kind) class Module(Sym): - def __init__(self, access_modifier, name, scope, is_pkg): + def __init__(self, access_modifier, name, scope, is_pkg, is_inline = False): super().__init__(access_modifier, name) scope.owner = self self.scope = scope self.is_pkg = is_pkg + self.is_inline = is_inline class Function(Sym): def __init__(self, access_modifier, name, args, scope): diff --git a/examples/sema.bs b/examples/sema.bs deleted file mode 100644 index 6ffbfa8..0000000 --- a/examples/sema.bs +++ /dev/null @@ -1,13 +0,0 @@ -mod syntax - -pub fn main() { - print("hello") -} - -pub(pkg) fn my_fn(a: number) {} - -mod my_module { - pub fn new_func() {} - pub fn other_func() {} -} - diff --git a/examples/syntax.bs b/examples/syntax.bs deleted file mode 100644 index 5495fdb..0000000 --- a/examples/syntax.bs +++ /dev/null @@ -1,95 +0,0 @@ -// #![extern_module(global=true)] - -extern pkg core - -// mod asset_manager - -use core::fs -use core::io as lua_io -use love::filesystem::* -use love::graphics::{ draw, draw_arc } - -use pkg::asset_manager::AssetManager - -mod tests { - use core::testing::assert - - fn test0() { - @assert(0x1 == -0x1 && true || false) - } -} - -fn require(path: string) any - -var my_var = 9 -var myvar2, myvar3 = (1, 2, 3) - -const ALPHA = 0b00101010110101 - -class Item {} - -pub fn sumtype(x: number | string | Item) {} - -enum WorldLevel { - overworld - midgard - underworld - undefined = 0xFF -} - -class Person { - name: string - age: number = 15 - - items: []Item = [Item()] - magic: [4]number = [1, 2, 3, 4]! - - map: {string:number} = { - "First Key": 99, - "Second Key": 100 - } - - tuple: (number, ?lua.string) = (1, nil) - - fn setAge(self, age: number) { - self.age = age - } - - fn mkdir(path: lua.string = ".") number { - @assert(path) - } -} - -fn main() { - var w = WorldLevel.midgard - w = .undefined - - var x = 666.0e4 - var a = 123 - var q, m, c = (99, true, nil) - var name = "StunxFS" - name = 999 - println("Hello World!") - _ = math.pow(2) - std.my_func() - if (true) q = 3 - if (false) { - s = if (true) { - 99 - } else if (99) { - 99 - } else { - 100 - } - } - while (nil) m += 4 - match (name) { - "holaa" -> m = 99, - "wb", "js" -> m = 1000, - else -> exit - } - match { - true -> true, - false -> false - } -}