mirror of
https://github.com/irmen/prog8.git
synced 2025-02-27 18:29:00 +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
|
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
|
from .symbols import FLOAT_MAX_POSITIVE, FLOAT_MAX_NEGATIVE, SourceRef, SymbolTable, SymbolError, PrimitiveType
|
||||||
|
|
||||||
|
|
||||||
@ -61,6 +61,45 @@ class SourceLine:
|
|||||||
return text
|
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, *,
|
def parse_expr_as_int(text: str, context: Optional[SymbolTable], ppcontext: Optional[SymbolTable], sourceref: SourceRef, *,
|
||||||
minimum: int=0, maximum: int=0xffff) -> int:
|
minimum: int=0, maximum: int=0xffff) -> int:
|
||||||
result = parse_expr_as_primitive(text, context, ppcontext, sourceref, minimum=minimum, maximum=maximum)
|
result = parse_expr_as_primitive(text, context, ppcontext, sourceref, minimum=minimum, maximum=maximum)
|
||||||
|
@ -12,9 +12,9 @@ import os
|
|||||||
import shutil
|
import shutil
|
||||||
import enum
|
import enum
|
||||||
from collections import defaultdict
|
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,\
|
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, \
|
from .symbols import SourceRef, SymbolTable, DataType, SymbolDefinition, SubroutineDef, LabelDef, \
|
||||||
zeropage, check_value_in_range, char_to_bytevalue, \
|
zeropage, check_value_in_range, char_to_bytevalue, \
|
||||||
PrimitiveType, VariableDef, ConstantDef, SymbolError, STRING_DATATYPES, \
|
PrimitiveType, VariableDef, ConstantDef, SymbolError, STRING_DATATYPES, \
|
||||||
@ -62,6 +62,8 @@ class ParseResult:
|
|||||||
return label
|
return label
|
||||||
|
|
||||||
def lookup(self, dottedname: str) -> Tuple[Optional['ParseResult.Block'], Optional[Union[SymbolDefinition, SymbolTable]]]:
|
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:
|
try:
|
||||||
scope, result = self.symbols.lookup(dottedname)
|
scope, result = self.symbols.lookup(dottedname)
|
||||||
return scope.owning_block, result
|
return scope.owning_block, result
|
||||||
@ -1044,15 +1046,7 @@ class Parser:
|
|||||||
argumentstr = argumentstr.strip() if argumentstr else ""
|
argumentstr = argumentstr.strip() if argumentstr else ""
|
||||||
arguments = None
|
arguments = None
|
||||||
if argumentstr:
|
if argumentstr:
|
||||||
arguments = []
|
arguments = parse_arguments(argumentstr, self.sourceref)
|
||||||
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))
|
|
||||||
target = None # type: ParseResult.Value
|
target = None # type: ParseResult.Value
|
||||||
if targetstr[0] == '[' and targetstr[-1] == ']':
|
if targetstr[0] == '[' and targetstr[-1] == ']':
|
||||||
# indirect call to address in register pair or memory location
|
# 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")
|
raise self.PError("cannot call that type of indirect symbol")
|
||||||
address = target.address if isinstance(target, ParseResult.MemMappedValue) else None
|
address = target.address if isinstance(target, ParseResult.MemMappedValue) else None
|
||||||
try:
|
try:
|
||||||
_, symbol = self.lookup(targetstr)
|
_, symbol = self.lookup_with_ppsymbols(targetstr)
|
||||||
except ParseError:
|
except ParseError:
|
||||||
symbol = None # it's probably a number or a register then
|
symbol = None # it's probably a number or a register then
|
||||||
if isinstance(symbol, SubroutineDef):
|
if isinstance(symbol, SubroutineDef):
|
||||||
@ -1080,10 +1074,9 @@ class Parser:
|
|||||||
args_with_pnames = []
|
args_with_pnames = []
|
||||||
for i, (argname, value) in enumerate(arguments or []):
|
for i, (argname, value) in enumerate(arguments or []):
|
||||||
pname, preg = symbol.parameters[i]
|
pname, preg = symbol.parameters[i]
|
||||||
if argname:
|
required_name = pname or preg
|
||||||
if argname != preg:
|
if argname and argname != required_name:
|
||||||
raise self.PError("parameter mismatch ({:s}, expected {:s})".format(argname, preg))
|
raise self.PError("parameter mismatch ('{:s}', expected '{:s}')".format(argname, required_name))
|
||||||
else:
|
|
||||||
argname = preg
|
argname = preg
|
||||||
args_with_pnames.append((argname, value))
|
args_with_pnames.append((argname, value))
|
||||||
arguments = args_with_pnames
|
arguments = args_with_pnames
|
||||||
@ -1111,14 +1104,17 @@ class Parser:
|
|||||||
return int(text[1:], 2)
|
return int(text[1:], 2)
|
||||||
return int(text)
|
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
|
# parses assigning a value to one or more targets
|
||||||
parts = line.split("=")
|
parts = line.split("=")
|
||||||
|
if parsed_rvalue:
|
||||||
|
r_value = parsed_rvalue
|
||||||
|
else:
|
||||||
rhs = parts.pop()
|
rhs = parts.pop()
|
||||||
|
r_value = self.parse_expression(rhs)
|
||||||
l_values = [self.parse_expression(part) for part in parts]
|
l_values = [self.parse_expression(part) for part in parts]
|
||||||
if any(isinstance(lv, ParseResult.IntegerValue) for lv in l_values):
|
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?")
|
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:
|
for lv in l_values:
|
||||||
assignable, reason = lv.assignable_from(r_value)
|
assignable, reason = lv.assignable_from(r_value)
|
||||||
if not assignable:
|
if not assignable:
|
||||||
@ -1162,8 +1158,7 @@ class Parser:
|
|||||||
if line.strip() == "}":
|
if line.strip() == "}":
|
||||||
return ParseResult.InlineAsm(lineno, asmlines)
|
return ParseResult.InlineAsm(lineno, asmlines)
|
||||||
# asm can refer to other symbols as well, track subroutine usage
|
# asm can refer to other symbols as well, track subroutine usage
|
||||||
if line.startswith((" ", "\t")):
|
splits = line.split(maxsplit=1)
|
||||||
splits = line.split(maxsplit=2)
|
|
||||||
if len(splits) == 2:
|
if len(splits) == 2:
|
||||||
for match in re.finditer(r"(?P<symbol>[a-zA-Z_$][a-zA-Z0-9_\.]+)", splits[1]):
|
for match in re.finditer(r"(?P<symbol>[a-zA-Z_$][a-zA-Z0-9_\.]+)", splits[1]):
|
||||||
name = match.group("symbol")
|
name = match.group("symbol")
|
||||||
@ -1172,7 +1167,7 @@ class Parser:
|
|||||||
try:
|
try:
|
||||||
if '.' not in name:
|
if '.' not in name:
|
||||||
name = self.cur_block.symbols.parent.name + '.' + name
|
name = self.cur_block.symbols.parent.name + '.' + name
|
||||||
_, symbol = self.lookup(name)
|
_, symbol = self.lookup_with_ppsymbols(name)
|
||||||
except ParseError:
|
except ParseError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
@ -1262,7 +1257,7 @@ class Parser:
|
|||||||
elif text == "false":
|
elif text == "false":
|
||||||
return ParseResult.IntegerValue(0)
|
return ParseResult.IntegerValue(0)
|
||||||
elif self.is_identifier(text):
|
elif self.is_identifier(text):
|
||||||
symblock, sym = self.lookup(text)
|
symblock, sym = self.lookup_with_ppsymbols(text)
|
||||||
if isinstance(sym, (VariableDef, ConstantDef)):
|
if isinstance(sym, (VariableDef, ConstantDef)):
|
||||||
constant = isinstance(sym, ConstantDef)
|
constant = isinstance(sym, ConstantDef)
|
||||||
if self.cur_block is symblock:
|
if self.cur_block is symblock:
|
||||||
@ -1340,14 +1335,14 @@ class Parser:
|
|||||||
return blockname.isidentifier() and name.isidentifier()
|
return blockname.isidentifier() and name.isidentifier()
|
||||||
return False
|
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)
|
symblock, sym = self.cur_block.lookup(dottedname)
|
||||||
if sym is None:
|
if sym is None and self.ppsymbols:
|
||||||
# symbol is not (yet) known in current block, see if the ppsymbols know about it
|
# symbol is not (yet) known, see if the symbols from the preprocess parse phase know about it
|
||||||
if '.' not in dottedname:
|
if '.' not in dottedname:
|
||||||
dottedname = self.cur_block.name + '.' + dottedname
|
dottedname = self.cur_block.name + '.' + dottedname
|
||||||
try:
|
try:
|
||||||
if self.ppsymbols:
|
|
||||||
symtable, sym = self.ppsymbols.lookup(dottedname)
|
symtable, sym = self.ppsymbols.lookup(dottedname)
|
||||||
assert dottedname.startswith(symtable.name)
|
assert dottedname.startswith(symtable.name)
|
||||||
symblock = None # the block might not have been parsed yet, so just return this instead
|
symblock = None # the block might not have been parsed yet, so just return this instead
|
||||||
|
@ -179,9 +179,12 @@ class SubroutineDef(SymbolDefinition):
|
|||||||
for _, param in parameters:
|
for _, param in parameters:
|
||||||
if param in REGISTER_BYTES:
|
if param in REGISTER_BYTES:
|
||||||
self.input_registers.add(param)
|
self.input_registers.add(param)
|
||||||
|
self.clobbered_registers.add(param)
|
||||||
elif param in REGISTER_WORDS:
|
elif param in REGISTER_WORDS:
|
||||||
self.input_registers.add(param[0])
|
self.input_registers.add(param[0])
|
||||||
self.input_registers.add(param[1])
|
self.input_registers.add(param[1])
|
||||||
|
self.clobbered_registers.add(param[0])
|
||||||
|
self.clobbered_registers.add(param[1])
|
||||||
else:
|
else:
|
||||||
raise SymbolError("invalid parameter spec: " + param)
|
raise SymbolError("invalid parameter spec: " + param)
|
||||||
for register in returnvalues:
|
for register in returnvalues:
|
||||||
@ -254,6 +257,10 @@ class SymbolTable:
|
|||||||
return symbolname in self.symbols
|
return symbolname in self.symbols
|
||||||
|
|
||||||
def lookup(self, dottedname: str, include_builtin_names: bool=False) -> Tuple['SymbolTable', Union[SymbolDefinition, 'SymbolTable']]:
|
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('.')
|
nameparts = dottedname.split('.')
|
||||||
if len(nameparts) == 1:
|
if len(nameparts) == 1:
|
||||||
try:
|
try:
|
||||||
@ -265,7 +272,7 @@ class SymbolTable:
|
|||||||
elif nameparts[0] in BUILTIN_SYMBOLS:
|
elif nameparts[0] in BUILTIN_SYMBOLS:
|
||||||
return self, getattr(builtins, nameparts[0])
|
return self, getattr(builtins, nameparts[0])
|
||||||
raise SymbolError("undefined symbol '{:s}'".format(nameparts[0])) from None
|
raise SymbolError("undefined symbol '{:s}'".format(nameparts[0])) from None
|
||||||
# start from toplevel namespace:
|
# restart from global namespace:
|
||||||
scope = self
|
scope = self
|
||||||
while scope.parent:
|
while scope.parent:
|
||||||
scope = scope.parent
|
scope = scope.parent
|
||||||
@ -278,7 +285,7 @@ class SymbolTable:
|
|||||||
if isinstance(scope, SymbolTable):
|
if isinstance(scope, SymbolTable):
|
||||||
return scope.lookup(nameparts[-1])
|
return scope.lookup(nameparts[-1])
|
||||||
elif isinstance(scope, SubroutineDef):
|
elif isinstance(scope, SubroutineDef):
|
||||||
return scope.sub_block.symbols.lookup(nameparts[-1])
|
return scope.sub_block.symbols.lookup_with_ppsymbols(nameparts[-1])
|
||||||
else:
|
else:
|
||||||
raise SymbolError("invalid block name '{:s}' in dotted name".format(namepart))
|
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_ZP1 = $02 ; scratch register #1 in ZP
|
||||||
memory SCRATCH_ZP2 = $03 ; scratch register #2 in ZP
|
memory SCRATCH_ZP2 = $03 ; scratch register #2 in ZP
|
||||||
|
|
||||||
memory COLOR = $0286 ; cursor color
|
memory .byte COLOR = $0286 ; cursor color
|
||||||
memory CINV = $0314 ; IRQ vector
|
memory .word CINV = $0314 ; IRQ vector
|
||||||
|
|
||||||
; ---- VIC-II registers ----
|
; ---- VIC-II registers ----
|
||||||
|
|
||||||
|
@ -251,7 +251,11 @@ The syntax is:
|
|||||||
... statements ...
|
... 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.
|
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
|
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.
|
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 sub1 () -> (X?) = $ffdd
|
||||||
sub sub2 (A) -> (Y?) = $eecc
|
sub sub2 (A) -> (Y?) = $eecc
|
||||||
|
sub sub3 (XY) -> (Y?) = $ddaa
|
||||||
|
sub sub4 (string: XY, other : A) -> (Y?) = $dd22
|
||||||
|
|
||||||
|
|
||||||
bar
|
bar
|
||||||
goto sub1
|
goto sub1
|
||||||
goto sub2 (1 )
|
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 bar ()
|
||||||
goto [AX]
|
goto [AX]
|
||||||
goto [AX] ()
|
goto [AX] ()
|
||||||
@ -47,6 +56,13 @@ bar
|
|||||||
|
|
||||||
sub1!()
|
sub1!()
|
||||||
sub2!(11)
|
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!()
|
bar!()
|
||||||
[XY] ! ()
|
[XY] ! ()
|
||||||
[var1] !()
|
[var1] !()
|
||||||
@ -65,6 +81,13 @@ bar
|
|||||||
|
|
||||||
sub1()
|
sub1()
|
||||||
sub2(11)
|
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 ()
|
bar ()
|
||||||
[AX]()
|
[AX]()
|
||||||
[var1] ( )
|
[var1] ( )
|
||||||
|
@ -7,18 +7,22 @@ import "c64lib"
|
|||||||
var .text name = "?"*80
|
var .text name = "?"*80
|
||||||
|
|
||||||
start
|
start
|
||||||
|
XY = c64.CINV
|
||||||
SI = 1
|
SI = 1
|
||||||
[$0314.word] = #irq_handler
|
c64.CINV = #irq_handler
|
||||||
SI = 0
|
SI = 0
|
||||||
|
|
||||||
c64util.print_string("enter your name: ")
|
c64util.print_string("enter your name: ")
|
||||||
c64util.input_chars(name)
|
c64util.input_chars(name)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
; c64util.print_string("thank you, ") ; @todo fix param parsing /splitting
|
c64util.print_string("thank you, mr or mrs: ")
|
||||||
c64util.print_string("thank you ")
|
|
||||||
c64util.print_string(name)
|
c64util.print_string(name)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
SI = 1
|
||||||
|
c64.CINV = XY
|
||||||
|
SI = 0
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
irq_handler
|
irq_handler
|
||||||
|
Loading…
x
Reference in New Issue
Block a user