diff --git a/bsc/AST.py b/bsc/AST.py index e4a0bdc..c663da7 100644 --- a/bsc/AST.py +++ b/bsc/AST.py @@ -27,10 +27,11 @@ def __str__(self): return f"Pos(file='{self.file}', line={self.line}, column={self.column}, len={self.len}, pos={self.pos})" class SourceFile: - def __init__(self, file, decls, mod_sym): + def __init__(self, file, decls, mod_sym, deps = []): self.file = file self.mod_sym = mod_sym self.decls = decls + self.deps = deps # Declarations diff --git a/bsc/__init__.py b/bsc/__init__.py index cab955a..9030990 100644 --- a/bsc/__init__.py +++ b/bsc/__init__.py @@ -4,8 +4,8 @@ import os -from bsc import sym, report -from bsc.AST import BasicType +from bsc import report, utils +from bsc.AST import BasicType, ModDecl from bsc.prefs import Prefs from bsc.astgen import AstGen from bsc.sema import Sema @@ -72,10 +72,11 @@ def compile(self): def import_modules(self): for sf in self.source_files: self.import_modules_from_decls(sf.mod_sym, sf.decls) + self.resolve_deps() def import_modules_from_decls(self, parent, decls): for decl in decls: - if isinstance(decl, AST.ModDecl): + if isinstance(decl, ModDecl): if decl.is_inline: self.import_modules_from_decls(parent, decl.decls) else: @@ -97,6 +98,40 @@ def import_module(self, parent, decl): else: report.error(f"module `{decl.name}` not found", decl.pos) + def resolve_deps(self): + g = self.module_graph() + g_resolved = g.resolve() + self.vlog( + f"-----= resolved dependencies graph =-----\n{g_resolved.display()}" + ) + self.vlog("-----------------------------------------") + cycles = g_resolved.display_cycles() + if len(cycles) > 1: + utils.error( + f"import cycle detected between the following modules:\n{cycles}" + ) + self.vlog("----------= imported modules =-----------") + for node in g_resolved.nodes: + self.vlog(f"> {node.name}") + self.vlog("-----------------------------------------") + source_files = self.source_files + self.source_files = [] + for node in g_resolved.nodes: + for sf in source_files: + if sf.mod_sym.name == node.name: + self.source_files.append(sf) + self.vlog("module dependencies resolved...") + + def module_graph(self): + g = utils.DepGraph() + for sf in self.source_files: + deps = [] + for dep in sf.deps: + dep.parent = sf.mod_sym + deps.append(dep.qualname()) + g.add(sf.mod_sym.qualname(), deps) + return g + def parse_input(self): self.parse_file(self.prefs.pkg_name, self.prefs.input, is_pkg = True) @@ -104,3 +139,8 @@ 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) ) + + def vlog(self, s): + if self.prefs.is_verbose: + bsc_log = utils.bold(utils.green("[bsc-log]")) + print(f"{bsc_log} {s}") diff --git a/bsc/astgen.py b/bsc/astgen.py index b6d83c8..0fea830 100644 --- a/bsc/astgen.py +++ b/bsc/astgen.py @@ -20,6 +20,7 @@ def __init__(self, ctx): self.ctx = ctx self.file = "" self.source_file = None + self.source_file_deps = [] self.mod_sym = None def parse_file(self, mod_name, file, is_pkg = False, parent_mod = None): @@ -30,7 +31,7 @@ def parse_file(self, mod_name, file, is_pkg = False, parent_mod = None): ) self.source_file = SourceFile( self.file, self.transform(bs_parser.parse(open(file).read())), - self.mod_sym + self.mod_sym, deps = self.source_file_deps ) try: if is_pkg: @@ -40,6 +41,7 @@ def parse_file(self, mod_name, file, is_pkg = False, parent_mod = None): parent_mod.scope.add_sym(self.source_file.mod_sym) except utils.CompilerError as e: utils.error(e.args[0]) + self.source_file_deps = [] return self.source_file def mkpos(self, token): @@ -61,19 +63,20 @@ def extern_pkg(self, *nodes): def mod_decl(self, *nodes): access_modifier = self.get_access_modifier(nodes) pos = self.mkpos(nodes[0] or nodes[1]) + name = nodes[2].name is_inline = nodes[3] != None decls = [] if is_inline: decls = list(nodes[4:-1]) - if not is_inline: + else: pos += nodes[2].pos mod_sym = Module( - access_modifier, nodes[2].name, Scope(self.mod_sym.scope, True), - False, is_inline - ) - return ModDecl( - access_modifier, nodes[2].name, is_inline, decls, pos, mod_sym + access_modifier, name, Scope(self.mod_sym.scope, True), False, + is_inline ) + if not is_inline: + self.source_file_deps.append(mod_sym) + return ModDecl(access_modifier, name, is_inline, decls, pos, mod_sym) def enum_decl(self, *nodes): pos = self.mkpos(nodes[1]) diff --git a/bsc/prefs.py b/bsc/prefs.py index f30800e..2fd31a1 100644 --- a/bsc/prefs.py +++ b/bsc/prefs.py @@ -12,24 +12,30 @@ def __init__(self): self.is_library = False self.pkg_name = "" + self.is_verbose = False def parse_args(self): parser = argparse.ArgumentParser( prog = 'bsc', description = 'The BlueScript compiler' ) parser.add_argument('INPUT', help = "the input file", nargs = 1) + parser.add_argument( + '--pkg-name', action = 'store', metavar = 'pkg_name', help = + 'specifies the name of the package being compiled (by default the name of the given file or directory will be used)' + ) parser.add_argument( '--lib', action = 'store_true', help = 'specifies whether the input is a library or not' ) parser.add_argument( - '--pkg-name', action = 'store', metavar = 'pkg_name', help = - 'specifies the name of the package being compiled (by default the name of the given file or directory will be used)' + '-v', '--verbose', action = 'store_true', + help = 'Enable verbosity in the compiler while compiling' ) args = parser.parse_args() self.is_library = args.lib self.pkg_name = args.pkg_name or "" + self.is_verbose = args.verbose # check input file self.input = args.INPUT[0] diff --git a/bsc/sema.py b/bsc/sema.py index afaf754..94144df 100644 --- a/bsc/sema.py +++ b/bsc/sema.py @@ -54,7 +54,6 @@ def check_mod_decl(self, decl): old_sym = self.cur_sym old_mod = self.cur_mod if self.first_pass: - decl.sym.parent = self.cur_sym if decl.is_inline: self.add_sym(decl.sym, decl.pos) self.cur_mod = decl.sym