diff --git a/compiler/nodes.py b/compiler/nodes.py index b9918a1..c466edf 100644 --- a/compiler/nodes.py +++ b/compiler/nodes.py @@ -316,7 +316,7 @@ class Variable(Node): def semantic_analysis(self, context: semantic.Context): super(Variable, self).semantic_analysis(context) - variable = context.get_variable(self.identifier.value) + variable = context.get_variable(self.identifier.value, reader=self) if variable is None: raise SemanticAnalysisError(location=self.location(), message=f"Unknown variable '{self.identifier.value}'") diff --git a/compiler/semantic.py b/compiler/semantic.py index b55e8b3..702c19f 100644 --- a/compiler/semantic.py +++ b/compiler/semantic.py @@ -15,7 +15,11 @@ class SymbolABC(abc.ABC): def __init__(self, context: Context, name: str, value: nodes.Value | None | Literal["builtin"] = None): self.context = context self.name = name - self.definitions = [value] + self.writes: list[nodes.Node] = [] + if isinstance(value, nodes.Node): + self.writes += [value] + + self.reads: list[nodes.Node] = [] self._repr_guard: bool = False def fully_qualified_name(self) -> str: @@ -28,14 +32,15 @@ class SymbolABC(abc.ABC): if self._repr_guard: return str(self) self._repr_guard = True - definitions = [str(d.location().begin) for d in self.writes if isinstance(d, nodes.Node)] + writes = [str(d.location().begin) for d in self.writes] + reads = [str(d.location().begin) for d in self.reads] self._repr_guard = False - return f"{str(self)} [definitions: {', '.join(definitions)}]" + return f"{str(self)} [writes: {', '.join(writes)}; reads: {', '.join(reads)}]" class Type(SymbolABC): def __init__(self, context: Context, name: str, value: nodes.Value | None | Literal["builtin"] = None): - super().__init__(context, name, value) + super().__init__(context, name, value=value) class Function(Type): @@ -44,8 +49,10 @@ class Function(Type): class Variable(SymbolABC): @typecheck - def __init__(self, context: Context, name: str, value: nodes.Value | None = None, typedef: Type | None = None): - super().__init__(context, name, value) + def __init__(self, context: Context, name: str, + value: nodes.Value | None = None, + typedef: Type | None = None): + super().__init__(context, name, value=value) self.type = typedef if typedef is not None else context.get_type("__unknown") def __str__(self): @@ -82,10 +89,18 @@ class Context: return None - def get_variable(self, name: str) -> Variable | None: - return self._resolve_symbol("variables", name) + @typecheck + def _get_symbol(self, attribute_name: str, name: str, reader: nodes.Node | None = None) -> SymbolABC | None: + node = self._resolve_symbol(attribute_name, name) + if node is not None and reader is not None: + node.reads += [reader] + return node - def define_variable(self, name: str, type_identifier: nodes.Identifier, value: nodes.Value) -> Variable: + def get_variable(self, name: str, reader: nodes.Node | None = None) -> Variable | None: + return self._get_symbol("variables", name, reader) + + def define_variable(self, name: str, + type_identifier: nodes.Identifier, value: nodes.Value) -> Variable: if name in self.variables: raise SemanticAnalysisError(value.location()) @@ -94,7 +109,7 @@ class Context: raise SemanticAnalysisError(location=type_identifier.location(), message=f"Unknown type '{type_identifier.value}'") - variable = Variable(self, name, value, typedef) + variable = Variable(self, name, value=value, typedef=typedef) self.variables[name] = variable @@ -107,7 +122,7 @@ class Context: raise SemanticAnalysisError(value.location(), message=f"Can't assign a value to undeclared variable '{name}'") variable = self.variables[name] - variable.definitions += [value] + variable.writes += [value] self.variables[name] = variable @@ -118,17 +133,17 @@ class Context: typedef: Type if name in self.types: typedef = self.types[name] - typedef.definitions += [value] + typedef.writes += [value] else: - typedef = Type(self, name, value) + typedef = Type(self, name, value=value) self.types[name] = typedef return typedef @typecheck - def get_type(self, name: str) -> Type | None: - return self._resolve_symbol("types", name) + def get_type(self, name: str, reader: nodes.Node | None = None) -> Type | None: + return self._get_symbol("types", name, reader) def add_context(self, context: Context) -> None: self.child_contexts[context.name] = context @@ -163,10 +178,10 @@ class BuiltinContext(Context): def __init__(self): super().__init__(name="builtins", parent=None) self.types = { - "__unknown": Type(self, "__unknown", "builtin"), - "__function": Function(self, "__function", "builtin"), - "uint32": Type(self, "uint32", "builtin") + "__unknown": Type(self, "__unknown", value="builtin"), + "__function": Function(self, "__function", value="builtin"), + "uint32": Type(self, "uint32", value="builtin") } self.variables = { - "display": Variable(self, "display", None, self.types["__function"]) + "display": Variable(self, "display", typedef=self.types["__function"]) }