mirror of
https://github.com/irmen/prog8.git
synced 2024-11-25 19:31:36 +00:00
fixed arg parsing
This commit is contained in:
parent
c78cbc4a33
commit
4a9d3200cd
@ -7,7 +7,7 @@ License: GNU GPL 3.0, see LICENSE
|
||||
"""
|
||||
|
||||
import ast
|
||||
from typing import Union, Optional
|
||||
from typing import Union, Optional, List, Tuple, Any
|
||||
from .symbols import FLOAT_MAX_POSITIVE, FLOAT_MAX_NEGATIVE, SourceRef, SymbolTable, SymbolError, PrimitiveType
|
||||
|
||||
|
||||
@ -61,6 +61,45 @@ class SourceLine:
|
||||
return text
|
||||
|
||||
|
||||
def parse_arguments(text: str, sourceref: SourceRef) -> List[Tuple[str, PrimitiveType]]:
|
||||
src = SourceLine(text, sourceref)
|
||||
text = src.preprocess()
|
||||
try:
|
||||
nodes = ast.parse("__func({:s})".format(text), sourceref.file, "eval")
|
||||
except SyntaxError as x:
|
||||
raise src.to_error(str(x))
|
||||
|
||||
def astnode_to_repr(node: ast.AST) -> str:
|
||||
if isinstance(node, ast.Name):
|
||||
return node.id
|
||||
if isinstance(node, ast.Num):
|
||||
return repr(node.n)
|
||||
if isinstance(node, ast.Str):
|
||||
return repr(node.s)
|
||||
if isinstance(node, ast.BinOp):
|
||||
if node.left.id == "__ptr" and isinstance(node.op, ast.MatMult):
|
||||
return '#' + astnode_to_repr(node.right)
|
||||
else:
|
||||
print("error", ast.dump(node))
|
||||
raise TypeError("invalid arg ast node type", node)
|
||||
if isinstance(node, ast.Attribute):
|
||||
return astnode_to_repr(node.value) + "." + node.attr
|
||||
print("error", ast.dump(node))
|
||||
raise TypeError("invalid arg ast node type", node)
|
||||
|
||||
args = [] # type: List[Tuple[str, Any]]
|
||||
if isinstance(nodes, ast.Expression):
|
||||
for arg in nodes.body.args:
|
||||
reprvalue = astnode_to_repr(arg)
|
||||
args.append((None, reprvalue))
|
||||
for kwarg in nodes.body.keywords:
|
||||
reprvalue = astnode_to_repr(kwarg.value)
|
||||
args.append((kwarg.arg, reprvalue))
|
||||
return args
|
||||
else:
|
||||
raise TypeError("ast.Expression expected")
|
||||
|
||||
|
||||
def parse_expr_as_int(text: str, context: Optional[SymbolTable], ppcontext: Optional[SymbolTable], sourceref: SourceRef, *,
|
||||
minimum: int=0, maximum: int=0xffff) -> int:
|
||||
result = parse_expr_as_primitive(text, context, ppcontext, sourceref, minimum=minimum, maximum=maximum)
|
||||
|
@ -12,9 +12,9 @@ import os
|
||||
import shutil
|
||||
import enum
|
||||
from collections import defaultdict
|
||||
from typing import Set, List, Tuple, Optional, Any, Dict, Union, Set
|
||||
from typing import Set, List, Tuple, Optional, Any, Dict, Union
|
||||
from .astparse import ParseError, parse_expr_as_int, parse_expr_as_number, parse_expr_as_primitive,\
|
||||
parse_expr_as_string
|
||||
parse_expr_as_string, parse_arguments
|
||||
from .symbols import SourceRef, SymbolTable, DataType, SymbolDefinition, SubroutineDef, LabelDef, \
|
||||
zeropage, check_value_in_range, char_to_bytevalue, \
|
||||
PrimitiveType, VariableDef, ConstantDef, SymbolError, STRING_DATATYPES, \
|
||||
@ -62,6 +62,8 @@ class ParseResult:
|
||||
return label
|
||||
|
||||
def lookup(self, dottedname: str) -> Tuple[Optional['ParseResult.Block'], Optional[Union[SymbolDefinition, SymbolTable]]]:
|
||||
# Searches a name in the current block or globally, if the name is scoped (=contains a '.').
|
||||
# Does NOT utilize a symbol table from a preprocessing parse phase, only looks in the current.
|
||||
try:
|
||||
scope, result = self.symbols.lookup(dottedname)
|
||||
return scope.owning_block, result
|
||||
@ -1044,15 +1046,7 @@ class Parser:
|
||||
argumentstr = argumentstr.strip() if argumentstr else ""
|
||||
arguments = None
|
||||
if argumentstr:
|
||||
arguments = []
|
||||
for part in argumentstr.split(','):
|
||||
pname, sep, pvalue = part.partition('=')
|
||||
pname = pname.strip()
|
||||
pvalue = pvalue.strip()
|
||||
if sep:
|
||||
arguments.append((pname, pvalue))
|
||||
else:
|
||||
arguments.append((None, pname))
|
||||
arguments = parse_arguments(argumentstr, self.sourceref)
|
||||
target = None # type: ParseResult.Value
|
||||
if targetstr[0] == '[' and targetstr[-1] == ']':
|
||||
# indirect call to address in register pair or memory location
|
||||
@ -1069,7 +1063,7 @@ class Parser:
|
||||
raise self.PError("cannot call that type of indirect symbol")
|
||||
address = target.address if isinstance(target, ParseResult.MemMappedValue) else None
|
||||
try:
|
||||
_, symbol = self.lookup(targetstr)
|
||||
_, symbol = self.lookup_with_ppsymbols(targetstr)
|
||||
except ParseError:
|
||||
symbol = None # it's probably a number or a register then
|
||||
if isinstance(symbol, SubroutineDef):
|
||||
@ -1080,11 +1074,10 @@ class Parser:
|
||||
args_with_pnames = []
|
||||
for i, (argname, value) in enumerate(arguments or []):
|
||||
pname, preg = symbol.parameters[i]
|
||||
if argname:
|
||||
if argname != preg:
|
||||
raise self.PError("parameter mismatch ({:s}, expected {:s})".format(argname, preg))
|
||||
else:
|
||||
argname = preg
|
||||
required_name = pname or preg
|
||||
if argname and argname != required_name:
|
||||
raise self.PError("parameter mismatch ('{:s}', expected '{:s}')".format(argname, required_name))
|
||||
argname = preg
|
||||
args_with_pnames.append((argname, value))
|
||||
arguments = args_with_pnames
|
||||
self.result.sub_used_by(symbol, self.sourceref)
|
||||
@ -1111,14 +1104,17 @@ class Parser:
|
||||
return int(text[1:], 2)
|
||||
return int(text)
|
||||
|
||||
def parse_assignment(self, line: str) -> ParseResult.AssignmentStmt:
|
||||
def parse_assignment(self, line: str, parsed_rvalue: ParseResult.Value = None) -> ParseResult.AssignmentStmt:
|
||||
# parses assigning a value to one or more targets
|
||||
parts = line.split("=")
|
||||
rhs = parts.pop()
|
||||
if parsed_rvalue:
|
||||
r_value = parsed_rvalue
|
||||
else:
|
||||
rhs = parts.pop()
|
||||
r_value = self.parse_expression(rhs)
|
||||
l_values = [self.parse_expression(part) for part in parts]
|
||||
if any(isinstance(lv, ParseResult.IntegerValue) for lv in l_values):
|
||||
raise self.PError("can't have a constant as assignment target, perhaps you wanted indirection [...] instead?")
|
||||
r_value = self.parse_expression(rhs)
|
||||
for lv in l_values:
|
||||
assignable, reason = lv.assignable_from(r_value)
|
||||
if not assignable:
|
||||
@ -1162,22 +1158,21 @@ class Parser:
|
||||
if line.strip() == "}":
|
||||
return ParseResult.InlineAsm(lineno, asmlines)
|
||||
# asm can refer to other symbols as well, track subroutine usage
|
||||
if line.startswith((" ", "\t")):
|
||||
splits = line.split(maxsplit=2)
|
||||
if len(splits) == 2:
|
||||
for match in re.finditer(r"(?P<symbol>[a-zA-Z_$][a-zA-Z0-9_\.]+)", splits[1]):
|
||||
name = match.group("symbol")
|
||||
if name[0] == '$':
|
||||
continue
|
||||
try:
|
||||
if '.' not in name:
|
||||
name = self.cur_block.symbols.parent.name + '.' + name
|
||||
_, symbol = self.lookup(name)
|
||||
except ParseError:
|
||||
pass
|
||||
else:
|
||||
if isinstance(symbol, SubroutineDef):
|
||||
self.result.sub_used_by(symbol, self.sourceref)
|
||||
splits = line.split(maxsplit=1)
|
||||
if len(splits) == 2:
|
||||
for match in re.finditer(r"(?P<symbol>[a-zA-Z_$][a-zA-Z0-9_\.]+)", splits[1]):
|
||||
name = match.group("symbol")
|
||||
if name[0] == '$':
|
||||
continue
|
||||
try:
|
||||
if '.' not in name:
|
||||
name = self.cur_block.symbols.parent.name + '.' + name
|
||||
_, symbol = self.lookup_with_ppsymbols(name)
|
||||
except ParseError:
|
||||
pass
|
||||
else:
|
||||
if isinstance(symbol, SubroutineDef):
|
||||
self.result.sub_used_by(symbol, self.sourceref)
|
||||
asmlines.append(line)
|
||||
|
||||
def parse_asminclude(self, line: str) -> ParseResult.InlineAsm:
|
||||
@ -1262,7 +1257,7 @@ class Parser:
|
||||
elif text == "false":
|
||||
return ParseResult.IntegerValue(0)
|
||||
elif self.is_identifier(text):
|
||||
symblock, sym = self.lookup(text)
|
||||
symblock, sym = self.lookup_with_ppsymbols(text)
|
||||
if isinstance(sym, (VariableDef, ConstantDef)):
|
||||
constant = isinstance(sym, ConstantDef)
|
||||
if self.cur_block is symblock:
|
||||
@ -1340,17 +1335,17 @@ class Parser:
|
||||
return blockname.isidentifier() and name.isidentifier()
|
||||
return False
|
||||
|
||||
def lookup(self, dottedname: str) -> Tuple[ParseResult.Block, Union[SymbolDefinition, SymbolTable]]:
|
||||
def lookup_with_ppsymbols(self, dottedname: str) -> Tuple[ParseResult.Block, Union[SymbolDefinition, SymbolTable]]:
|
||||
# Tries to find a symbol, if it cannot be located, the symbol table from the preprocess parse phase is consulted as well
|
||||
symblock, sym = self.cur_block.lookup(dottedname)
|
||||
if sym is None:
|
||||
# symbol is not (yet) known in current block, see if the ppsymbols know about it
|
||||
if sym is None and self.ppsymbols:
|
||||
# symbol is not (yet) known, see if the symbols from the preprocess parse phase know about it
|
||||
if '.' not in dottedname:
|
||||
dottedname = self.cur_block.name + '.' + dottedname
|
||||
try:
|
||||
if self.ppsymbols:
|
||||
symtable, sym = self.ppsymbols.lookup(dottedname)
|
||||
assert dottedname.startswith(symtable.name)
|
||||
symblock = None # the block might not have been parsed yet, so just return this instead
|
||||
symtable, sym = self.ppsymbols.lookup(dottedname)
|
||||
assert dottedname.startswith(symtable.name)
|
||||
symblock = None # the block might not have been parsed yet, so just return this instead
|
||||
except (LookupError, SymbolError) as x:
|
||||
raise self.PError(str(x))
|
||||
return symblock, sym
|
||||
|
@ -179,9 +179,12 @@ class SubroutineDef(SymbolDefinition):
|
||||
for _, param in parameters:
|
||||
if param in REGISTER_BYTES:
|
||||
self.input_registers.add(param)
|
||||
self.clobbered_registers.add(param)
|
||||
elif param in REGISTER_WORDS:
|
||||
self.input_registers.add(param[0])
|
||||
self.input_registers.add(param[1])
|
||||
self.clobbered_registers.add(param[0])
|
||||
self.clobbered_registers.add(param[1])
|
||||
else:
|
||||
raise SymbolError("invalid parameter spec: " + param)
|
||||
for register in returnvalues:
|
||||
@ -254,6 +257,10 @@ class SymbolTable:
|
||||
return symbolname in self.symbols
|
||||
|
||||
def lookup(self, dottedname: str, include_builtin_names: bool=False) -> Tuple['SymbolTable', Union[SymbolDefinition, 'SymbolTable']]:
|
||||
# Tries to find the dottedname in the current symbol table (if it is not scoped),
|
||||
# or globally if it is scoped (=contains a '.'). If required, math and builtin symbols
|
||||
# such as 'sin' or 'max' are also resolved.
|
||||
# Does NOT utilize a symbol table from a preprocessing parse phase, only looks in the current.
|
||||
nameparts = dottedname.split('.')
|
||||
if len(nameparts) == 1:
|
||||
try:
|
||||
@ -265,7 +272,7 @@ class SymbolTable:
|
||||
elif nameparts[0] in BUILTIN_SYMBOLS:
|
||||
return self, getattr(builtins, nameparts[0])
|
||||
raise SymbolError("undefined symbol '{:s}'".format(nameparts[0])) from None
|
||||
# start from toplevel namespace:
|
||||
# restart from global namespace:
|
||||
scope = self
|
||||
while scope.parent:
|
||||
scope = scope.parent
|
||||
@ -278,7 +285,7 @@ class SymbolTable:
|
||||
if isinstance(scope, SymbolTable):
|
||||
return scope.lookup(nameparts[-1])
|
||||
elif isinstance(scope, SubroutineDef):
|
||||
return scope.sub_block.symbols.lookup(nameparts[-1])
|
||||
return scope.sub_block.symbols.lookup_with_ppsymbols(nameparts[-1])
|
||||
else:
|
||||
raise SymbolError("invalid block name '{:s}' in dotted name".format(namepart))
|
||||
|
||||
|
@ -10,8 +10,8 @@ output raw
|
||||
memory SCRATCH_ZP1 = $02 ; scratch register #1 in ZP
|
||||
memory SCRATCH_ZP2 = $03 ; scratch register #2 in ZP
|
||||
|
||||
memory COLOR = $0286 ; cursor color
|
||||
memory CINV = $0314 ; IRQ vector
|
||||
memory .byte COLOR = $0286 ; cursor color
|
||||
memory .word CINV = $0314 ; IRQ vector
|
||||
|
||||
; ---- VIC-II registers ----
|
||||
|
||||
|
@ -251,7 +251,11 @@ The syntax is:
|
||||
... statements ...
|
||||
}
|
||||
|
||||
proc_parameters = comma separated list of "<parametername>:<register>" pairs specifying the input parameters
|
||||
proc_parameters = comma separated list of "<parametername>:<register>" pairs specifying the input parameters.
|
||||
You can omit the parameter names as long as the arguments "line up".
|
||||
(actually, the Python parameter passing rules apply, so you can also mix positional
|
||||
and keyword arguments, as long as the keyword arguments come last)
|
||||
|
||||
proc_results = comma separated list of <register> names specifying in which register(s) the output is returned.
|
||||
If the register name ends with a '?', that means the register doesn't contain a real return value but
|
||||
is clobbered in the process so the original value it had before calling the sub is no longer valid.
|
||||
|
@ -17,11 +17,20 @@
|
||||
|
||||
sub sub1 () -> (X?) = $ffdd
|
||||
sub sub2 (A) -> (Y?) = $eecc
|
||||
sub sub3 (XY) -> (Y?) = $ddaa
|
||||
sub sub4 (string: XY, other : A) -> (Y?) = $dd22
|
||||
|
||||
|
||||
bar
|
||||
goto sub1
|
||||
goto sub2 (1 )
|
||||
goto sub3 (3)
|
||||
goto sub3 (XY="hello")
|
||||
goto sub3 ("hello, there")
|
||||
goto sub4 (string="hello, there", other = 42)
|
||||
goto sub4 ("hello", 42)
|
||||
goto sub4 ("hello", other= 42)
|
||||
goto sub4 (string="hello", other = 42)
|
||||
goto bar ()
|
||||
goto [AX]
|
||||
goto [AX] ()
|
||||
@ -47,6 +56,13 @@ bar
|
||||
|
||||
sub1!()
|
||||
sub2!(11)
|
||||
sub3 !(3)
|
||||
sub3! (XY="hello")
|
||||
sub3! ("hello, there")
|
||||
sub4! ("hello", 42)
|
||||
sub4! ("hello", other=42)
|
||||
sub4! (string="hello", other = 42)
|
||||
sub4! (string="hello, there", other = 42)
|
||||
bar!()
|
||||
[XY] ! ()
|
||||
[var1] !()
|
||||
@ -65,6 +81,13 @@ bar
|
||||
|
||||
sub1()
|
||||
sub2(11)
|
||||
sub3 (3)
|
||||
sub3 (XY="hello")
|
||||
sub3 ("hello, there")
|
||||
sub4 ("hello", 42)
|
||||
sub4 ("hello", other= 42)
|
||||
sub4 (string="hello", other = 42)
|
||||
sub4 (string="hello, there", other = 42)
|
||||
bar ()
|
||||
[AX]()
|
||||
[var1] ( )
|
||||
|
@ -7,18 +7,22 @@ import "c64lib"
|
||||
var .text name = "?"*80
|
||||
|
||||
start
|
||||
XY = c64.CINV
|
||||
SI = 1
|
||||
[$0314.word] = #irq_handler
|
||||
c64.CINV = #irq_handler
|
||||
SI = 0
|
||||
|
||||
c64util.print_string("enter your name: ")
|
||||
c64util.input_chars(name)
|
||||
c64.CHROUT('\n')
|
||||
; c64util.print_string("thank you, ") ; @todo fix param parsing /splitting
|
||||
c64util.print_string("thank you ")
|
||||
c64util.print_string("thank you, mr or mrs: ")
|
||||
c64util.print_string(name)
|
||||
c64.CHROUT('\n')
|
||||
|
||||
SI = 1
|
||||
c64.CINV = XY
|
||||
SI = 0
|
||||
|
||||
return
|
||||
|
||||
irq_handler
|
||||
|
Loading…
Reference in New Issue
Block a user