semantic+nodes: record variable reads
This commit is contained in:
parent
f08d697699
commit
88526aa5e6
2 changed files with 35 additions and 20 deletions
|
|
@ -316,7 +316,7 @@ class Variable(Node):
|
||||||
|
|
||||||
def semantic_analysis(self, context: semantic.Context):
|
def semantic_analysis(self, context: semantic.Context):
|
||||||
super(Variable, self).semantic_analysis(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:
|
if variable is None:
|
||||||
raise SemanticAnalysisError(location=self.location(), message=f"Unknown variable '{self.identifier.value}'")
|
raise SemanticAnalysisError(location=self.location(), message=f"Unknown variable '{self.identifier.value}'")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,11 @@ class SymbolABC(abc.ABC):
|
||||||
def __init__(self, context: Context, name: str, value: nodes.Value | None | Literal["builtin"] = None):
|
def __init__(self, context: Context, name: str, value: nodes.Value | None | Literal["builtin"] = None):
|
||||||
self.context = context
|
self.context = context
|
||||||
self.name = name
|
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
|
self._repr_guard: bool = False
|
||||||
|
|
||||||
def fully_qualified_name(self) -> str:
|
def fully_qualified_name(self) -> str:
|
||||||
|
|
@ -28,14 +32,15 @@ class SymbolABC(abc.ABC):
|
||||||
if self._repr_guard:
|
if self._repr_guard:
|
||||||
return str(self)
|
return str(self)
|
||||||
self._repr_guard = True
|
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
|
self._repr_guard = False
|
||||||
return f"{str(self)} [definitions: {', '.join(definitions)}]"
|
return f"{str(self)} [writes: {', '.join(writes)}; reads: {', '.join(reads)}]"
|
||||||
|
|
||||||
|
|
||||||
class Type(SymbolABC):
|
class Type(SymbolABC):
|
||||||
def __init__(self, context: Context, name: str, value: nodes.Value | None | Literal["builtin"] = None):
|
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):
|
class Function(Type):
|
||||||
|
|
@ -44,8 +49,10 @@ class Function(Type):
|
||||||
|
|
||||||
class Variable(SymbolABC):
|
class Variable(SymbolABC):
|
||||||
@typecheck
|
@typecheck
|
||||||
def __init__(self, context: Context, name: str, value: nodes.Value | None = None, typedef: Type | None = None):
|
def __init__(self, context: Context, name: str,
|
||||||
super().__init__(context, name, value)
|
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")
|
self.type = typedef if typedef is not None else context.get_type("__unknown")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|
@ -82,10 +89,18 @@ class Context:
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_variable(self, name: str) -> Variable | None:
|
@typecheck
|
||||||
return self._resolve_symbol("variables", name)
|
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:
|
if name in self.variables:
|
||||||
raise SemanticAnalysisError(value.location())
|
raise SemanticAnalysisError(value.location())
|
||||||
|
|
||||||
|
|
@ -94,7 +109,7 @@ class Context:
|
||||||
raise SemanticAnalysisError(location=type_identifier.location(),
|
raise SemanticAnalysisError(location=type_identifier.location(),
|
||||||
message=f"Unknown type '{type_identifier.value}'")
|
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
|
self.variables[name] = variable
|
||||||
|
|
||||||
|
|
@ -107,7 +122,7 @@ class Context:
|
||||||
raise SemanticAnalysisError(value.location(),
|
raise SemanticAnalysisError(value.location(),
|
||||||
message=f"Can't assign a value to undeclared variable '{name}'")
|
message=f"Can't assign a value to undeclared variable '{name}'")
|
||||||
variable = self.variables[name]
|
variable = self.variables[name]
|
||||||
variable.definitions += [value]
|
variable.writes += [value]
|
||||||
|
|
||||||
self.variables[name] = variable
|
self.variables[name] = variable
|
||||||
|
|
||||||
|
|
@ -118,17 +133,17 @@ class Context:
|
||||||
typedef: Type
|
typedef: Type
|
||||||
if name in self.types:
|
if name in self.types:
|
||||||
typedef = self.types[name]
|
typedef = self.types[name]
|
||||||
typedef.definitions += [value]
|
typedef.writes += [value]
|
||||||
else:
|
else:
|
||||||
typedef = Type(self, name, value)
|
typedef = Type(self, name, value=value)
|
||||||
|
|
||||||
self.types[name] = typedef
|
self.types[name] = typedef
|
||||||
|
|
||||||
return typedef
|
return typedef
|
||||||
|
|
||||||
@typecheck
|
@typecheck
|
||||||
def get_type(self, name: str) -> Type | None:
|
def get_type(self, name: str, reader: nodes.Node | None = None) -> Type | None:
|
||||||
return self._resolve_symbol("types", name)
|
return self._get_symbol("types", name, reader)
|
||||||
|
|
||||||
def add_context(self, context: Context) -> None:
|
def add_context(self, context: Context) -> None:
|
||||||
self.child_contexts[context.name] = context
|
self.child_contexts[context.name] = context
|
||||||
|
|
@ -163,10 +178,10 @@ class BuiltinContext(Context):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(name="builtins", parent=None)
|
super().__init__(name="builtins", parent=None)
|
||||||
self.types = {
|
self.types = {
|
||||||
"__unknown": Type(self, "__unknown", "builtin"),
|
"__unknown": Type(self, "__unknown", value="builtin"),
|
||||||
"__function": Function(self, "__function", "builtin"),
|
"__function": Function(self, "__function", value="builtin"),
|
||||||
"uint32": Type(self, "uint32", "builtin")
|
"uint32": Type(self, "uint32", value="builtin")
|
||||||
}
|
}
|
||||||
self.variables = {
|
self.variables = {
|
||||||
"display": Variable(self, "display", None, self.types["__function"])
|
"display": Variable(self, "display", typedef=self.types["__function"])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue