mirror of
https://github.com/irmen/prog8.git
synced 2024-09-11 15:55:55 +00:00
builtin functions are now defined as BuiltinFunction in topmost scope
This commit is contained in:
parent
d299742ddf
commit
9b23bfb85c
@ -31,6 +31,7 @@ class PlyParser:
|
|||||||
try:
|
try:
|
||||||
module = parse_file(filename, self.lexer_error)
|
module = parse_file(filename, self.lexer_error)
|
||||||
self.check_directives(module)
|
self.check_directives(module)
|
||||||
|
module.scope.define_builtin_functions()
|
||||||
self.process_imports(module)
|
self.process_imports(module)
|
||||||
self.check_all_symbolnames(module)
|
self.check_all_symbolnames(module)
|
||||||
self.create_multiassigns(module)
|
self.create_multiassigns(module)
|
||||||
@ -431,7 +432,6 @@ class PlyParser:
|
|||||||
if not filename:
|
if not filename:
|
||||||
raise ParseError("imported file not found", directive.sourceref)
|
raise ParseError("imported file not found", directive.sourceref)
|
||||||
imported_module, import_parse_errors = self.import_file(filename)
|
imported_module, import_parse_errors = self.import_file(filename)
|
||||||
imported_module.scope.parent_scope = module.scope
|
|
||||||
imported.append(imported_module)
|
imported.append(imported_module)
|
||||||
self.parse_errors += import_parse_errors
|
self.parse_errors += import_parse_errors
|
||||||
if not self.imported_module:
|
if not self.imported_module:
|
||||||
@ -439,7 +439,6 @@ class PlyParser:
|
|||||||
filename = self.find_import_file("il65lib", module.sourceref.file)
|
filename = self.find_import_file("il65lib", module.sourceref.file)
|
||||||
if filename:
|
if filename:
|
||||||
imported_module, import_parse_errors = self.import_file(filename)
|
imported_module, import_parse_errors = self.import_file(filename)
|
||||||
imported_module.scope.parent_scope = module.scope
|
|
||||||
imported.append(imported_module)
|
imported.append(imported_module)
|
||||||
self.parse_errors += import_parse_errors
|
self.parse_errors += import_parse_errors
|
||||||
else:
|
else:
|
||||||
|
@ -30,7 +30,7 @@ class ZpOptions(enum.Enum):
|
|||||||
CLOBBER_RESTORE = "clobber_restore"
|
CLOBBER_RESTORE = "clobber_restore"
|
||||||
|
|
||||||
|
|
||||||
math_functions = {name: func for name, func in vars(math).items() if inspect.isbuiltin(func)}
|
math_functions = {name: func for name, func in vars(math).items() if inspect.isbuiltin(func) and name != "pow"}
|
||||||
builtin_functions = {name: func for name, func in vars(builtins).items() if inspect.isbuiltin(func)}
|
builtin_functions = {name: func for name, func in vars(builtins).items() if inspect.isbuiltin(func)}
|
||||||
|
|
||||||
|
|
||||||
@ -121,7 +121,6 @@ class Scope(AstNode):
|
|||||||
nodes = attr.ib(type=list, init=True) # requires nodes in __init__
|
nodes = attr.ib(type=list, init=True) # requires nodes in __init__
|
||||||
symbols = attr.ib(init=False)
|
symbols = attr.ib(init=False)
|
||||||
name = attr.ib(init=False) # will be set by enclosing block, or subroutine etc.
|
name = attr.ib(init=False) # will be set by enclosing block, or subroutine etc.
|
||||||
parent_scope = attr.ib(init=False, default=None) # will be wired up later
|
|
||||||
_save_registers = attr.ib(type=bool, default=None, init=False)
|
_save_registers = attr.ib(type=bool, default=None, init=False)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -137,6 +136,13 @@ class Scope(AstNode):
|
|||||||
def save_registers(self, save: bool) -> None:
|
def save_registers(self, save: bool) -> None:
|
||||||
self._save_registers = save
|
self._save_registers = save
|
||||||
|
|
||||||
|
@property
|
||||||
|
def parent_scope(self) -> Optional['Scope']:
|
||||||
|
parent_scope = self.parent
|
||||||
|
while parent_scope and not isinstance(parent_scope, Scope):
|
||||||
|
parent_scope = parent_scope.parent
|
||||||
|
return parent_scope
|
||||||
|
|
||||||
def __attrs_post_init__(self):
|
def __attrs_post_init__(self):
|
||||||
# populate the symbol table for this scope for fast lookups via scope.lookup("name") or scope.lookup("dotted.name")
|
# populate the symbol table for this scope for fast lookups via scope.lookup("name") or scope.lookup("dotted.name")
|
||||||
self.symbols = {}
|
self.symbols = {}
|
||||||
@ -147,28 +153,38 @@ class Scope(AstNode):
|
|||||||
def _populate_symboltable(self, node: AstNode) -> None:
|
def _populate_symboltable(self, node: AstNode) -> None:
|
||||||
if isinstance(node, (Label, VarDef)):
|
if isinstance(node, (Label, VarDef)):
|
||||||
if node.name in self.symbols:
|
if node.name in self.symbols:
|
||||||
raise ParseError("symbol already defined at {}".format(self.symbols[node.name].sourceref), node.sourceref)
|
raise ParseError("symbol '{}' already defined at {}".format(node.name, self.symbols[node.name].sourceref), node.sourceref)
|
||||||
self.symbols[node.name] = node
|
self.symbols[node.name] = node
|
||||||
if isinstance(node, Subroutine):
|
elif isinstance(node, (Subroutine, BuiltinFunction)):
|
||||||
if node.name in self.symbols:
|
if node.name in self.symbols:
|
||||||
raise ParseError("symbol already defined at {}".format(self.symbols[node.name].sourceref), node.sourceref)
|
raise ParseError("symbol '{}' already defined at {}".format(node.name, self.symbols[node.name].sourceref), node.sourceref)
|
||||||
self.symbols[node.name] = node
|
self.symbols[node.name] = node
|
||||||
if node.scope:
|
elif isinstance(node, Block):
|
||||||
node.scope.parent_scope = self
|
|
||||||
if isinstance(node, Block):
|
|
||||||
if node.name:
|
if node.name:
|
||||||
if node.name != "ZP" and node.name in self.symbols:
|
if node.name != "ZP" and node.name in self.symbols:
|
||||||
raise ParseError("symbol already defined at {}".format(self.symbols[node.name].sourceref), node.sourceref)
|
raise ParseError("symbol '{}' already defined at {}"
|
||||||
|
.format(node.name, self.symbols[node.name].sourceref), node.sourceref)
|
||||||
self.symbols[node.name] = node
|
self.symbols[node.name] = node
|
||||||
node.scope.parent_scope = self
|
|
||||||
|
@no_type_check
|
||||||
|
def define_builtin_functions(self) -> None:
|
||||||
|
for name, func in math_functions.items():
|
||||||
|
f = BuiltinFunction(name=name, func=func, sourceref=self.sourceref)
|
||||||
|
self.add_node(f)
|
||||||
|
for name, func in builtin_functions.items():
|
||||||
|
f = BuiltinFunction(name=name, func=func, sourceref=self.sourceref)
|
||||||
|
self.add_node(f)
|
||||||
|
|
||||||
def lookup(self, name: str) -> AstNode:
|
def lookup(self, name: str) -> AstNode:
|
||||||
assert isinstance(name, str)
|
assert isinstance(name, str)
|
||||||
if '.' in name:
|
if '.' in name:
|
||||||
# look up the dotted name starting from the topmost scope
|
# look up the dotted name starting from the topmost scope
|
||||||
scope = self
|
scope = self
|
||||||
while scope.parent_scope:
|
node = self
|
||||||
scope = scope.parent_scope
|
while node.parent:
|
||||||
|
if isinstance(node.parent, Scope):
|
||||||
|
scope = node.parent
|
||||||
|
node = node.parent
|
||||||
for namepart in name.split('.'):
|
for namepart in name.split('.'):
|
||||||
if isinstance(scope, (Block, Subroutine)):
|
if isinstance(scope, (Block, Subroutine)):
|
||||||
scope = scope.scope
|
scope = scope.scope
|
||||||
@ -182,8 +198,11 @@ class Scope(AstNode):
|
|||||||
# find the name in nested scope hierarchy
|
# find the name in nested scope hierarchy
|
||||||
if name in self.symbols:
|
if name in self.symbols:
|
||||||
return self.symbols[name]
|
return self.symbols[name]
|
||||||
if self.parent_scope:
|
parent_scope = self.parent
|
||||||
return self.parent_scope.lookup(name)
|
while parent_scope and not isinstance(parent_scope, Scope):
|
||||||
|
parent_scope = parent_scope.parent
|
||||||
|
if parent_scope:
|
||||||
|
return parent_scope.lookup(name)
|
||||||
raise UndefinedSymbolError("undefined symbol: " + name)
|
raise UndefinedSymbolError("undefined symbol: " + name)
|
||||||
|
|
||||||
def remove_node(self, node: AstNode) -> None:
|
def remove_node(self, node: AstNode) -> None:
|
||||||
@ -387,6 +406,15 @@ class DatatypeNode(AstNode):
|
|||||||
}[self.name]
|
}[self.name]
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(cmp=False, repr=False)
|
||||||
|
class BuiltinFunction(AstNode):
|
||||||
|
# This is a pseudo-node that will be artificially injected in the top-most scope,
|
||||||
|
# to represent all supported built-in functions or math-functions.
|
||||||
|
# No child nodes.
|
||||||
|
name = attr.ib(type=str)
|
||||||
|
func = attr.ib(type=callable)
|
||||||
|
|
||||||
|
|
||||||
@attr.s(cmp=False, repr=False)
|
@attr.s(cmp=False, repr=False)
|
||||||
class Subroutine(AstNode):
|
class Subroutine(AstNode):
|
||||||
# one subnode: the Scope.
|
# one subnode: the Scope.
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
import math
|
||||||
import pytest
|
import pytest
|
||||||
from il65.plylex import lexer, tokens, find_tok_column, literals, reserved, SourceRef
|
from il65.plylex import lexer, tokens, find_tok_column, literals, reserved, SourceRef
|
||||||
from il65.plyparse import parser, connect_parents, TokenFilter, Module, Subroutine, Block, IncrDecr, Scope, \
|
from il65.plyparse import parser, connect_parents, TokenFilter, Module, Subroutine, Block, IncrDecr, Scope, \
|
||||||
VarDef, Register, ExpressionWithOperator, LiteralValue, Label, SubCall, Dereference
|
VarDef, Register, ExpressionWithOperator, LiteralValue, Label, SubCall, Dereference,\
|
||||||
from il65.datatypes import DataType
|
BuiltinFunction, UndefinedSymbolError
|
||||||
|
from il65.datatypes import DataType, VarType
|
||||||
|
|
||||||
|
|
||||||
def lexer_error(sourceref: SourceRef, fmtstring: str, *args: str) -> None:
|
def lexer_error(sourceref: SourceRef, fmtstring: str, *args: str) -> None:
|
||||||
@ -296,3 +298,58 @@ def test_incrdecr():
|
|||||||
IncrDecr(operator="??", sourceref=sref)
|
IncrDecr(operator="??", sourceref=sref)
|
||||||
i = IncrDecr(operator="++", sourceref=sref)
|
i = IncrDecr(operator="++", sourceref=sref)
|
||||||
assert i.howmuch == 1
|
assert i.howmuch == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_symbol_lookup():
|
||||||
|
sref = SourceRef("test", 1, 1)
|
||||||
|
var1 = VarDef(name="var1", vartype="const", datatype=DataType.WORD, sourceref=sref)
|
||||||
|
var1.value = LiteralValue(value=42, sourceref=sref)
|
||||||
|
var1.value.parent = var1
|
||||||
|
var2 = VarDef(name="var2", vartype="const", datatype=DataType.FLOAT, sourceref=sref)
|
||||||
|
var2.value = LiteralValue(value=123.456, sourceref=sref)
|
||||||
|
var2.value.parent = var2
|
||||||
|
label1 = Label(name="outerlabel", sourceref=sref)
|
||||||
|
label2 = Label(name="innerlabel", sourceref=sref)
|
||||||
|
scope_inner = Scope(nodes=[
|
||||||
|
label2,
|
||||||
|
var2
|
||||||
|
], level="block", sourceref=sref)
|
||||||
|
scope_inner.name = "inner"
|
||||||
|
var2.parent = label2.parent = scope_inner
|
||||||
|
scope_outer = Scope(nodes=[
|
||||||
|
label1,
|
||||||
|
var1,
|
||||||
|
scope_inner
|
||||||
|
], level="block", sourceref=sref)
|
||||||
|
scope_outer.name = "outer"
|
||||||
|
scope_outer.define_builtin_functions()
|
||||||
|
var1.parent = label1.parent = scope_inner.parent = scope_outer
|
||||||
|
assert scope_inner.parent_scope is scope_outer
|
||||||
|
assert scope_outer.parent_scope is None
|
||||||
|
assert label1.my_scope() is scope_outer
|
||||||
|
assert var1.my_scope() is scope_outer
|
||||||
|
assert scope_inner.my_scope() is scope_outer
|
||||||
|
assert label2.my_scope() is scope_inner
|
||||||
|
assert var2.my_scope() is scope_inner
|
||||||
|
with pytest.raises(LookupError):
|
||||||
|
scope_outer.my_scope()
|
||||||
|
with pytest.raises(UndefinedSymbolError):
|
||||||
|
scope_inner.lookup("unexisting")
|
||||||
|
with pytest.raises(UndefinedSymbolError):
|
||||||
|
scope_outer.lookup("unexisting")
|
||||||
|
assert scope_inner.lookup("innerlabel") is label2
|
||||||
|
assert scope_inner.lookup("var2") is var2
|
||||||
|
assert scope_inner.lookup("outerlabel") is label1
|
||||||
|
assert scope_inner.lookup("var1") is var1
|
||||||
|
with pytest.raises(UndefinedSymbolError):
|
||||||
|
scope_outer.lookup("innerlabel")
|
||||||
|
with pytest.raises(UndefinedSymbolError):
|
||||||
|
scope_outer.lookup("var2")
|
||||||
|
assert scope_outer.lookup("var1") is var1
|
||||||
|
assert scope_outer.lookup("outerlabel") is label1
|
||||||
|
math_func = scope_inner.lookup("sin")
|
||||||
|
assert isinstance(math_func, BuiltinFunction)
|
||||||
|
assert math_func.name == "sin" and math_func.func is math.sin
|
||||||
|
builtin_func = scope_inner.lookup("max")
|
||||||
|
assert isinstance(builtin_func, BuiltinFunction)
|
||||||
|
assert builtin_func.name == "max" and builtin_func.func is max
|
||||||
|
Loading…
Reference in New Issue
Block a user