semantic+nodes: record variable reads

This commit is contained in:
Antoine Viallon 2023-05-24 00:19:07 +02:00
parent f08d697699
commit 88526aa5e6
Signed by: aviallon
GPG key ID: D126B13AB555E16F
2 changed files with 35 additions and 20 deletions

View file

@ -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}'")

View file

@ -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"])
}