semantic: add semantic checks for variables
Check read before first assignment Check unused variables
This commit is contained in:
parent
bcca6d4a6e
commit
f060608e73
1 changed files with 29 additions and 1 deletions
|
|
@ -5,10 +5,11 @@ from typing import Literal
|
||||||
|
|
||||||
from . import nodes
|
from . import nodes
|
||||||
from .errors import SemanticAnalysisError
|
from .errors import SemanticAnalysisError
|
||||||
from .logger import Logger
|
from .logger import Logger, Tracer, LogLevel
|
||||||
from .typechecking import typecheck
|
from .typechecking import typecheck
|
||||||
|
|
||||||
logger = Logger(__name__)
|
logger = Logger(__name__)
|
||||||
|
tracer = Tracer(logger=logger, level=LogLevel.Debug)
|
||||||
|
|
||||||
|
|
||||||
class SymbolABC(abc.ABC):
|
class SymbolABC(abc.ABC):
|
||||||
|
|
@ -181,6 +182,33 @@ class Context:
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return self._pprint()
|
return self._pprint()
|
||||||
|
|
||||||
|
@tracer.trace_method
|
||||||
|
def check(self) -> None:
|
||||||
|
for variable in self.variables.values():
|
||||||
|
|
||||||
|
# Check for reads before assignments
|
||||||
|
for read in variable.reads:
|
||||||
|
previous_write = None
|
||||||
|
for write in variable.writes:
|
||||||
|
if write.location() < read.location():
|
||||||
|
previous_write = write
|
||||||
|
break
|
||||||
|
if previous_write is None:
|
||||||
|
message = f"Reading undefined variable {variable.name}"
|
||||||
|
if len(variable.writes) > 0:
|
||||||
|
first_write = min(variable.writes, key=lambda w: w.location())
|
||||||
|
message += f" (first assignment is at {first_write.location().begin})"
|
||||||
|
raise SemanticAnalysisError(location=read.location(),
|
||||||
|
message=message)
|
||||||
|
|
||||||
|
# Check for unused variables
|
||||||
|
if len(variable.reads) == 0 and variable.definition is not None:
|
||||||
|
raise SemanticAnalysisError(variable.definition.location(),
|
||||||
|
message=f"Variable '{variable.name}' is unused")
|
||||||
|
|
||||||
|
for context in self.child_contexts.values():
|
||||||
|
context.check()
|
||||||
|
|
||||||
|
|
||||||
class BuiltinContext(Context):
|
class BuiltinContext(Context):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue