plyparsing more or less done

This commit is contained in:
Irmen de Jong 2018-01-07 02:36:27 +01:00
parent d9c62c2149
commit 16b95cf3e9
12 changed files with 378 additions and 238 deletions

View File

@ -7,6 +7,7 @@ License: GNU GPL 3.0, see LICENSE
"""
import ast
import attr
from typing import Union, Optional, List, Tuple, Any
from .symbols import FLOAT_MAX_POSITIVE, FLOAT_MAX_NEGATIVE, SourceRef, SymbolTable, SymbolError, PrimitiveType
@ -52,7 +53,7 @@ class SourceLine:
if c == '$' and self.text[i + 1] in "0123456789abcdefABCDEF":
text += "0x"
continue
if c == '#':
if c == '&':
if i > 0:
text += " "
text += "__ptr@"
@ -172,11 +173,7 @@ class EvaluatingTransformer(ast.NodeTransformer):
self.ppcontext = ppcontext
def error(self, message: str, column: int=0) -> ParseError:
if column:
ref = self.src.sourceref.copy()
ref.column = column
else:
ref = self.src.sourceref
ref = attr.evolve(self.src.sourceref, column=column)
return ParseError(message, self.src.text, ref)
def evaluate(self, node: ast.Expression) -> PrimitiveType:
@ -267,7 +264,7 @@ def astnode_to_repr(node: ast.AST) -> str:
return repr(node.s)
if isinstance(node, ast.BinOp):
if node.left.id == "__ptr" and isinstance(node.op, ast.MatMult): # type: ignore
return '#' + astnode_to_repr(node.right)
return '&' + astnode_to_repr(node.right)
else:
print("error", ast.dump(node))
raise TypeError("invalid arg ast node type", node)

View File

@ -1,6 +1,14 @@
"""
Programming Language for 6502/6510 microprocessors
This is the lexer of the IL65 code, that generates a stream of tokens for the parser.
Written by Irmen de Jong (irmen@razorvine.net)
License: GNU GPL 3.0, see LICENSE
"""
import sys
from .symbols import SourceRef
import ply.lex
from .symbols import SourceRef
# token names

View File

@ -10,6 +10,7 @@ import re
import os
import sys
import shutil
import attr
from collections import defaultdict
from typing import Set, List, Tuple, Optional, Dict, Union, Generator
from .exprparse import ParseError, parse_expr_as_int, parse_expr_as_number, parse_expr_as_primitive,\
@ -235,8 +236,7 @@ class Parser:
def _parse_2(self) -> None:
# parsing pass 2 (not done during preprocessing!)
self.cur_block = None
self.sourceref.line = -1
self.sourceref.column = 0
self.sourceref = SourceRef(self.sourceref.file, -1)
def imm_string_to_var(stmt: AssignmentStmt, containing_block: Block) -> None:
if stmt.right.name or not isinstance(stmt.right, StringValue):
@ -257,13 +257,13 @@ class Parser:
def desugar_immediate_strings(stmt: AstNode, containing_block: Block) -> None:
if isinstance(stmt, CallStmt):
for s in stmt.desugared_call_arguments:
self.sourceref = s.sourceref.copy()
self.sourceref = s.sourceref
imm_string_to_var(s, containing_block)
for s in stmt.desugared_output_assignments:
self.sourceref = s.sourceref.copy()
self.sourceref = s.sourceref
imm_string_to_var(s, containing_block)
if isinstance(stmt, AssignmentStmt):
self.sourceref = stmt.sourceref.copy()
self.sourceref = stmt.sourceref
imm_string_to_var(stmt, containing_block)
def desugar_immediate_floats(stmt: AstNode, containing_block: Block) -> None:
@ -312,11 +312,10 @@ class Parser:
for block in self.result.blocks:
self.cur_block = block
self.sourceref = block.sourceref.copy()
self.sourceref.column = 0
self.sourceref = attr.evolve(block.sourceref, column=0)
for _, sub, stmt in block.all_statements():
if isinstance(stmt, CallStmt):
self.sourceref = stmt.sourceref.copy()
self.sourceref = stmt.sourceref
self.desugar_call_arguments_and_outputs(stmt)
desugar_immediate_strings(stmt, self.cur_block)
desugar_immediate_floats(stmt, self.cur_block)
@ -327,7 +326,7 @@ class Parser:
for name, value in stmt.arguments or []:
assert name is not None, "all call arguments should have a name or be matched on a named parameter"
assignment = self.parse_assignment(name, value)
assignment.sourceref = stmt.sourceref.copy()
assignment.sourceref = stmt.sourceref
if assignment.leftvalues[0].datatype != DataType.BYTE:
if isinstance(assignment.right, IntegerValue) and assignment.right.constant:
# a call that doesn't expect a BYTE argument but gets one, converted from a 1-byte string most likely
@ -360,15 +359,16 @@ class Parser:
def next_line(self) -> str:
self._cur_lineidx += 1
try:
self.sourceref.line, line = self.lines[self._cur_lineidx]
self.sourceref.column = 0
lineno, line = self.lines[self._cur_lineidx]
self.sourceref = SourceRef(file=self.sourceref.file, line=lineno)
return line
except IndexError:
return ""
def prev_line(self) -> str:
self._cur_lineidx -= 1
self.sourceref.line, line = self.lines[self._cur_lineidx]
lineno, line = self.lines[self._cur_lineidx]
self.sourceref = SourceRef(file=self.sourceref.file, line=lineno)
return line
def peek_next_line(self) -> str:
@ -506,6 +506,8 @@ class Parser:
_, filename = line.split(maxsplit=1)
except ValueError:
raise self.PError("invalid import statement")
if filename[0] in "'\"" and filename[-1] in "'\"":
filename = filename[1:-1]
if not filename:
raise self.PError("invalid filename")
self._parse_import_file(filename)
@ -554,7 +556,7 @@ class Parser:
raise self.PError("expected '~' (block)")
block_args = line[1:].split()
arg = ""
self.cur_block = Block("", self.sourceref.copy(), self.root_scope, self.result.preserve_registers)
self.cur_block = Block("", self.sourceref, self.root_scope, self.result.preserve_registers)
is_zp_block = False
while block_args:
arg = block_args.pop(0)
@ -568,7 +570,7 @@ class Parser:
raise self.PError("duplicate block name '{:s}', original definition at {}".format(arg, orig.sourceref))
self.cur_block = orig # zero page block occurrences are merged
else:
self.cur_block = Block(arg, self.sourceref.copy(), self.root_scope, self.result.preserve_registers)
self.cur_block = Block(arg, self.sourceref, self.root_scope, self.result.preserve_registers)
try:
self.root_scope.define_scope(self.cur_block.symbols, self.cur_block.sourceref)
except SymbolError as x:
@ -1040,10 +1042,15 @@ class Parser:
return InplaceIncrStmt(l_value, r_value.negative(), self.sourceref)
return AugmentedAssignmentStmt(l_value, operator, r_value, self.sourceref)
def parse_return(self, line: str) -> ReturnStmt:
def parse_return(self, line: str) -> Union[ReturnStmt, CallStmt]:
parts = line.split(maxsplit=1)
if parts[0] != "return":
raise self.PError("invalid statement, return expected")
if '(' in line and line[-1] == ')':
# assume it's a function call that follows the 'return'. Turn it into a goto.
parts[0] = "goto"
line = " ".join(parts)
return self.parse_statement(line) # type: ignore
a = x = y = None
values = [] # type: List[str]
if len(parts) > 1:
@ -1057,7 +1064,7 @@ class Parser:
if len(values) > 2:
y = self.parse_expression(values[2]) if values[2] else None
if len(values) > 3:
raise self.PError("too many returnvalues")
raise self.PError("too many returnvalues (>3)")
return ReturnStmt(self.sourceref, a, x, y)
def parse_asm(self) -> InlineAsm:
@ -1089,14 +1096,16 @@ class Parser:
asmlines.append(line)
def parse_asminclude(self, line: str) -> InlineAsm:
line = line.replace(",", " ")
aline = line.split()
if len(aline) < 2:
raise self.PError("invalid asminclude or asmbinary directive")
filename = aline[1]
if filename[0] not in "'\"" or filename[-1] not in "'\"":
raise self.PError("invalid filename, should use quotes")
filename = filename[1:-1]
if not filename:
raise self.PError("invalid filename")
if filename[0] in "'\"" or filename[-1] in "'\"":
raise self.PError("invalid filename, should not use quotes")
filename_in_sourcedir = os.path.join(os.path.split(self.sourceref.file)[0], filename)
filename_in_output_location = os.path.join(self.outputdir, filename)
if not os.path.isfile(filename_in_sourcedir):
@ -1131,7 +1140,7 @@ class Parser:
text = text.strip()
if not text:
raise self.PError("value expected")
if text[0] == '#':
if text[0] == '&':
if is_indirect:
raise self.PError("using the address-of something in an indirect value makes no sense")
# take the pointer (memory address) from the thing that follows this

View File

@ -1,26 +1,34 @@
"""
Programming Language for 6502/6510 microprocessors
This is the parser of the IL65 code, that generates a parse tree.
Written by Irmen de Jong (irmen@razorvine.net)
License: GNU GPL 3.0, see LICENSE
"""
from typing import List, Any
from ply.yacc import yacc
from .symbols import SourceRef, AstNode
from .lexer import tokens, lexer, find_tok_column # get the lexer tokens. required.
start = "start"
class Module(AstNode):
def __init__(self, nodes, sourceref):
def __init__(self, nodes: List[AstNode], sourceref: SourceRef) -> None:
super().__init__(sourceref)
self.nodes = nodes or []
class Directive(AstNode):
def __init__(self, name, args, sourceref):
def __init__(self, name: str, args, sourceref: SourceRef) -> None:
super().__init__(sourceref)
self.name = name
self.args = args or []
class Block(AstNode):
def __init__(self, name, address, scope, sourceref):
def __init__(self, name: str, address, scope, sourceref: SourceRef) -> None:
super().__init__(sourceref)
self.name = name
self.address = address
@ -28,58 +36,72 @@ class Block(AstNode):
class Scope(AstNode):
def __init__(self, nodes, sourceref):
def __init__(self, nodes: List[AstNode], sourceref: SourceRef) -> None:
super().__init__(sourceref)
self.nodes = nodes
class Label(AstNode):
def __init__(self, name, sourceref):
def __init__(self, name: str, sourceref: SourceRef) -> None:
super().__init__(sourceref)
self.name = name
class Register(AstNode):
def __init__(self, name, sourceref):
def __init__(self, name: str, sourceref: SourceRef) -> None:
super().__init__(sourceref)
self.name = name
class PreserveRegs(AstNode):
def __init__(self, registers, sourceref):
def __init__(self, registers, sourceref: SourceRef) -> None:
super().__init__(sourceref)
self.registers = registers
class Assignment(AstNode):
def __init__(self, lhs, operator, rhs, sourceref):
def __init__(self, lhs, rhs, sourceref: SourceRef) -> None:
super().__init__(sourceref)
self.lhs = lhs
self.operator = operator
self.rhs = rhs
class AugAssignment(Assignment):
def __init__(self, lhs, operator: str, rhs, sourceref: SourceRef) -> None:
super().__init__(lhs, rhs, sourceref)
self.operator = operator
class SubCall(AstNode):
def __init__(self, target, arguments, sourceref):
def __init__(self, target, preserveregs, arguments, sourceref: SourceRef) -> None:
super().__init__(sourceref)
self.target = target
self.preserveregs = preserveregs
self.arguments = arguments
class Return(AstNode):
def __init__(self, value, sourceref):
def __init__(self, valueA, valueX, valueY, sourceref: SourceRef) -> None:
super().__init__(sourceref)
self.value = value
self.valueA = valueA
self.valueX = valueX
self.valueY = valueY
class TargetRegisters(AstNode):
def __init__(self, registers, sourceref: SourceRef) -> None:
super().__init__(sourceref)
self.registers = registers
class InlineAssembly(AstNode):
def __init__(self, assembly, sourceref):
def __init__(self, assembly: str, sourceref: SourceRef) -> None:
super().__init__(sourceref)
self.assembly = assembly
class VarDef(AstNode):
def __init__(self, name, vartype, datatype, value, sourceref):
def __init__(self, name: str, vartype, datatype, value, sourceref: SourceRef) -> None:
super().__init__(sourceref)
self.name = name
self.vartype = vartype
@ -88,14 +110,14 @@ class VarDef(AstNode):
class Datatype(AstNode):
def __init__(self, name, dimension, sourceref):
def __init__(self, name: str, dimension, sourceref: SourceRef) -> None:
super().__init__(sourceref)
self.name = name
self.dimension = dimension
class Subroutine(AstNode):
def __init__(self, name, paramspec, resultspec, code, sourceref):
def __init__(self, name: str, paramspec, resultspec, code, sourceref: SourceRef) -> None:
super().__init__(sourceref)
self.name = name
self.paramspec = paramspec
@ -104,7 +126,7 @@ class Subroutine(AstNode):
class Goto(AstNode):
def __init__(self, target, ifstmt, condition, sourceref):
def __init__(self, target, ifstmt, condition, sourceref: SourceRef) -> None:
super().__init__(sourceref)
self.target = target
self.ifstmt = ifstmt
@ -112,7 +134,7 @@ class Goto(AstNode):
class Dereference(AstNode):
def __init__(self, location, datatype, sourceref):
def __init__(self, location, datatype, sourceref: SourceRef) -> None:
super().__init__(sourceref)
self.location = location
self.datatype = datatype
@ -126,43 +148,41 @@ class CallTarget(AstNode):
class CallArgument(AstNode):
def __init__(self, name, value, sourceref):
def __init__(self, name: str, value, sourceref: SourceRef) -> None:
super().__init__(sourceref)
self.name = name
self.value = value
class UnaryOp(AstNode):
def __init__(self, operator, operand, sourceref):
def __init__(self, operator, operand, sourceref: SourceRef) -> None:
super().__init__(sourceref)
self.operator = operator
self.operand = operand
class BinaryOp(AstNode):
def __init__(self, operator, left, right, sourceref):
class Expression(AstNode):
def __init__(self, lhs, operator, rhs, sourceref: SourceRef) -> None:
super().__init__(sourceref)
self.lhs = lhs
self.operator = operator
self.left = left
self.right = right
class Integer(AstNode):
def __init__(self, value, sourceref):
super().__init__(sourceref)
self.value = value
self.rhs = rhs
def p_start(p):
"""start : empty
| module_elements"""
"""
start : empty
| module_elements
"""
if p[1]:
p[0] = Module(p[1], _token_sref(p, 1))
def p_module(p):
"""module_elements : module_elt
| module_elements module_elt"""
"""
module_elements : module_elt
| module_elements module_elt
"""
if len(p) == 2:
p[0] = [p[1]]
else:
@ -170,15 +190,18 @@ def p_module(p):
def p_module_elt(p):
"""module_elt : ENDL
| directive
| block """
"""
module_elt : ENDL
| directive
| block
"""
p[0] = p[1]
def p_directive(p):
"""directive : DIRECTIVE ENDL
| DIRECTIVE directive_args ENDL
"""
directive : DIRECTIVE ENDL
| DIRECTIVE directive_args ENDL
"""
if len(p) == 2:
p[0] = Directive(p[1], None, _token_sref(p, 1))
@ -187,8 +210,9 @@ def p_directive(p):
def p_directive_args(p):
"""directive_args : directive_arg
| directive_args ',' directive_arg
"""
directive_args : directive_arg
| directive_args ',' directive_arg
"""
if len(p) == 2:
p[0] = [p[1]]
@ -197,48 +221,63 @@ def p_directive_args(p):
def p_directive_arg(p):
"""directive_arg : NAME
| INTEGER
| STRING
"""
directive_arg : NAME
| INTEGER
| STRING
"""
p[0] = p[1]
def p_block_name_addr(p):
"""block : BITINVERT NAME INTEGER endl_opt scope"""
"""
block : BITINVERT NAME INTEGER endl_opt scope
"""
p[0] = Block(p[2], p[3], p[5], _token_sref(p, 1))
def p_block_name(p):
"""block : BITINVERT NAME endl_opt scope"""
"""
block : BITINVERT NAME endl_opt scope
"""
p[0] = Block(p[2], None, p[4], _token_sref(p, 1))
def p_block(p):
"""block : BITINVERT endl_opt scope"""
"""
block : BITINVERT endl_opt scope
"""
p[0] = Block(None, None, p[3], _token_sref(p, 1))
def p_endl_opt(p):
"""endl_opt : empty
| ENDL"""
p[0] = p[1]
"""
endl_opt : empty
| ENDL
"""
pass
def p_scope(p):
"""scope : '{' scope_elements_opt '}'"""
"""
scope : '{' scope_elements_opt '}'
"""
p[0] = Scope(p[2], _token_sref(p, 1))
def p_scope_elements_opt(p):
"""scope_elements_opt : empty
| scope_elements"""
"""
scope_elements_opt : empty
| scope_elements
"""
p[0] = p[1]
def p_scope_elements(p):
"""scope_elements : scope_element
| scope_elements scope_element"""
"""
scope_elements : scope_element
| scope_elements scope_element
"""
if len(p) == 2:
p[0] = [p[1]]
else:
@ -246,49 +285,63 @@ def p_scope_elements(p):
def p_scope_element(p):
"""scope_element : ENDL
| label
| directive
| vardef
| subroutine
| inlineasm
| statement"""
"""
scope_element : ENDL
| label
| directive
| vardef
| subroutine
| inlineasm
| statement
"""
p[0] = p[1]
def p_label(p):
"""label : LABEL"""
"""
label : LABEL
"""
p[0] = Label(p[1], _token_sref(p, 1))
def p_inlineasm(p):
"""inlineasm : INLINEASM ENDL"""
"""
inlineasm : INLINEASM ENDL
"""
p[0] = InlineAssembly(p[1], _token_sref(p, 1))
def p_vardef(p):
"""vardef : VARTYPE type_opt NAME ENDL"""
"""
vardef : VARTYPE type_opt NAME ENDL
"""
p[0] = VarDef(p[3], p[1], p[2], None, _token_sref(p, 1))
def p_vardef_value(p):
"""vardef : VARTYPE type_opt NAME IS expression"""
"""
vardef : VARTYPE type_opt NAME IS expression
"""
p[0] = VarDef(p[3], p[1], p[2], p[5], _token_sref(p, 1))
def p_type_opt(p):
"""type_opt : DATATYPE '(' dimensions ')'
| DATATYPE
| empty"""
if len(p) == 4:
"""
type_opt : DATATYPE '(' dimensions ')'
| DATATYPE
| empty
"""
if len(p) == 5:
p[0] = Datatype(p[1], p[3], _token_sref(p, 1))
elif len(p) == 2:
p[0] = Datatype(p[1], None, _token_sref(p, 1))
def p_dimensions(p):
"""dimensions : INTEGER
| dimensions ',' INTEGER"""
"""
dimensions : INTEGER
| dimensions ',' INTEGER
"""
if len(p) == 2:
p[0] = [p[1]]
else:
@ -305,28 +358,36 @@ def p_literal_value(p):
def p_subroutine(p):
"""subroutine : SUB NAME '(' sub_param_spec ')' RARROW '(' sub_result_spec ')' subroutine_body ENDL"""
p[0] = Subroutine(p[2], p[4], p[8], p[10], _token_sref(p, 1))
"""
subroutine : SUB NAME '(' sub_param_spec ')' RARROW '(' sub_result_spec ')' subroutine_body ENDL
"""
p[0] = Subroutine(p[1], p[3], p[7], p[9], _token_sref(p, 1))
def p_sub_param_spec(p):
"""sub_param_spec : empty
| sub_param_list"""
"""
sub_param_spec : empty
| sub_param_list
"""
p[0] = p[1]
def p_sub_param_list(p):
"""sub_param_list : sub_param
| sub_param_list ',' sub_param"""
"""
sub_param_list : sub_param
| sub_param_list ',' sub_param
"""
if len(p) == 2:
p[0] = [p[1]]
else:
p[0] = p[1] + [p[2]]
p[0] = p[1] + [p[3]]
def p_sub_param(p):
"""sub_param : LABEL REGISTER
| REGISTER"""
"""
sub_param : LABEL REGISTER
| REGISTER
"""
if len(p) == 3:
p[0] = (p[1], p[2])
elif len(p) == 2:
@ -334,17 +395,22 @@ def p_sub_param(p):
def p_sub_result_spec(p):
"""sub_result_spec : empty
| '?'
| sub_result_list"""
"""
sub_result_spec : empty
| '?'
| sub_result_list
"""
if p[1] == '?':
p[0] = ['A', 'X', 'Y'] # '?' means: all registers clobbered
p[0] = p[1]
else:
p[0] = p[1]
def p_sub_result_list(p):
"""sub_result_list : sub_result_reg
| sub_result_list ',' sub_result_reg"""
"""
sub_result_list : sub_result_reg
| sub_result_list ',' sub_result_reg
"""
if len(p) == 2:
p[0] = [p[1]]
else:
@ -352,14 +418,18 @@ def p_sub_result_list(p):
def p_sub_result_reg(p):
"""sub_result_reg : REGISTER
| CLOBBEREDREGISTER"""
"""
sub_result_reg : REGISTER
| CLOBBEREDREGISTER
"""
p[0] = p[1]
def p_subroutine_body(p):
"""subroutine_body : scope
| IS INTEGER"""
"""
subroutine_body : scope
| IS INTEGER
"""
if len(p) == 2:
p[0] = p[1]
else:
@ -367,47 +437,61 @@ def p_subroutine_body(p):
def p_statement(p):
"""statement : assignment ENDL
| subroutine_call ENDL
| goto ENDL
| conditional_goto ENDL
| incrdecr ENDL
| return ENDL
"""
statement : assignment ENDL
| aug_assignment ENDL
| subroutine_call ENDL
| goto ENDL
| conditional_goto ENDL
| incrdecr ENDL
| return ENDL
"""
p[0] = p[1]
def p_incrdecr(p):
"""incrdecr : assignment_target INCR
| assignment_target DECR"""
"""
incrdecr : assignment_target INCR
| assignment_target DECR
"""
p[0] = UnaryOp(p[2], p[1], _token_sref(p, 1))
def p_call_subroutine(p):
"""subroutine_call : calltarget preserveregs_opt '(' call_arguments_opt ')'"""
p[0] = SubCall(p[1], p[3], _token_sref(p, 1))
"""
subroutine_call : calltarget preserveregs_opt '(' call_arguments_opt ')'
"""
p[0] = SubCall(p[1], p[2], p[4], _token_sref(p, 1))
def p_preserveregs_opt(p):
"""preserveregs_opt : empty
| preserveregs"""
"""
preserveregs_opt : empty
| preserveregs
"""
p[0] = p[1]
def p_preserveregs(p):
"""preserveregs : PRESERVEREGS"""
"""
preserveregs : PRESERVEREGS
"""
p[0] = PreserveRegs(p[1], _token_sref(p, 1))
def p_call_arguments_opt(p):
"""call_arguments_opt : empty
| call_arguments"""
"""
call_arguments_opt : empty
| call_arguments
"""
p[0] = p[1]
def p_call_arguments(p):
"""call_arguments : call_argument
| call_arguments ',' call_argument"""
"""
call_arguments : call_argument
| call_arguments ',' call_argument
"""
if len(p) == 2:
p[0] = [p[1]]
else:
@ -415,49 +499,69 @@ def p_call_arguments(p):
def p_call_argument(p):
"""call_argument : expression
| register IS expression
| NAME IS expression"""
"""
call_argument : expression
| register IS expression
| NAME IS expression
"""
if len(p) == 2:
p[0] = CallArgument(None, p[1], _token_sref(p, 1))
elif len(p) == 3:
elif len(p) == 4:
p[0] = CallArgument(p[1], p[3], _token_sref(p, 1))
def p_return(p):
"""return : RETURN
| RETURN expression"""
"""
return : RETURN
| RETURN expression
| RETURN expression ',' expression
| RETURN expression ',' expression ',' expression
"""
if len(p) == 2:
p[0] = Return(None, _token_sref(p, 1))
elif len(p) == 4:
p[0] = Return(p[3], _token_sref(p, 1))
p[0] = Return(None, None, None, _token_sref(p, 1))
elif len(p) == 3:
p[0] = Return(p[2], None, None, _token_sref(p, 1))
elif len(p) == 5:
p[0] = Return(p[2], p[4], None, _token_sref(p, 1))
elif len(p) == 7:
p[0] = Return(p[2], p[4], p[6], _token_sref(p, 1))
def p_register(p):
"""register : REGISTER"""
"""
register : REGISTER
"""
p[0] = Register(p[1], _token_sref(p, 1))
def p_goto(p):
"""goto : GOTO calltarget"""
"""
goto : GOTO calltarget
"""
p[0] = Goto(p[2], None, None, _token_sref(p, 1))
def p_conditional_goto_plain(p):
"""conditional_goto : IF GOTO calltarget"""
"""
conditional_goto : IF GOTO calltarget
"""
p[0] = Goto(p[3], p[1], None, _token_sref(p, 1))
def p_conditional_goto_expr(p):
"""conditional_goto : IF expression GOTO calltarget"""
"""
conditional_goto : IF expression GOTO calltarget
"""
p[0] = Goto(p[4], p[1], p[2], _token_sref(p, 1))
def p_calltarget(p):
"""calltarget : symbolname
| INTEGER
| BITAND symbolname
| dereference"""
"""
calltarget : symbolname
| INTEGER
| BITAND symbolname
| dereference
"""
if len(p) == 2:
p[0] = CallTarget(p[1], False, _token_sref(p, 1))
elif len(p) == 3:
@ -465,32 +569,42 @@ def p_calltarget(p):
def p_dereference(p):
"""dereference : '[' dereference_operand ']' """
"""
dereference : '[' dereference_operand ']'
"""
p[0] = Dereference(p[2][0], p[2][1], _token_sref(p, 1))
def p_dereference_operand(p):
"""dereference_operand : symbolname type_opt
| REGISTER type_opt
| INTEGER type_opt"""
"""
dereference_operand : symbolname type_opt
| REGISTER type_opt
| INTEGER type_opt
"""
p[0] = (p[1], p[2])
def p_symbolname(p):
"""symbolname : NAME
| DOTTEDNAME"""
"""
symbolname : NAME
| DOTTEDNAME
"""
p[0] = p[1]
def p_assignment(p):
"""assignment : assignment_lhs assignment_operator expression"""
p[0] = Assignment(p[1], p[2], p[3], _token_sref(p, 1))
"""
assignment : assignment_target IS expression
| assignment_target IS assignment
"""
p[0] = Assignment(p[1], p[3], _token_sref(p, 1))
def p_assignment_operator(p):
"""assignment_operator : IS
| AUGASSIGN"""
p[0] = p[1]
def p_aug_assignment(p):
"""
aug_assignment : assignment_target AUGASSIGN expression
"""
p[0] = AugAssignment(p[1], p[2], p[3], _token_sref(p, 1))
precedence = (
@ -503,69 +617,87 @@ precedence = (
def p_expression(p):
"""expression : expression '+' expression
| expression '-' expression
| expression '*' expression
| expression '/' expression
| expression LT expression
| expression GT expression
| expression LE expression
| expression GE expression
| expression EQUALS expression
| expression NOTEQUALS expression"""
pass
"""
expression : expression '+' expression
| expression '-' expression
| expression '*' expression
| expression '/' expression
| expression LT expression
| expression GT expression
| expression LE expression
| expression GE expression
| expression EQUALS expression
| expression NOTEQUALS expression
"""
p[0] = Expression(p[1], p[2], p[3], _token_sref(p, 1))
def p_expression_uminus(p):
"""expression : '-' expression %prec UNARY_MINUS"""
pass
"""
expression : '-' expression %prec UNARY_MINUS
"""
p[0] = UnaryOp(p[1], p[2], _token_sref(p, 1))
def p_expression_addressof(p):
"""expression : BITAND symbolname %prec UNARY_ADDRESSOF"""
pass
"""
expression : BITAND symbolname %prec UNARY_ADDRESSOF
"""
p[0] = UnaryOp(p[1], p[2], _token_sref(p, 1))
def p_unary_expression_bitinvert(p):
"""expression : BITINVERT expression"""
pass
"""
expression : BITINVERT expression
"""
p[0] = UnaryOp(p[1], p[2], _token_sref(p, 1))
def p_expression_group(p):
"""expression : '(' expression ')'"""
"""
expression : '(' expression ')'
"""
p[0] = p[2]
def p_expression_ass_rhs(p):
"""expression : expression_value"""
"""expression : expression_value"""
p[0] = p[1]
def p_expression_value(p):
"""expression_value : literal_value
| symbolname
| register
| subroutine_call
| dereference"""
"""
expression_value : literal_value
| symbolname
| register
| subroutine_call
| dereference
"""
p[0] = p[1]
def p_assignment_lhs(p):
"""assignment_lhs : assignment_target
| assignment_lhs ',' assignment_target"""
if len(p) == 2:
p[0] = [p[1]]
else:
p[0] = p[1] + [p[2]]
def p_assignment_target(p):
"""assignment_target : register
| symbolname
| dereference"""
"""
assignment_target : target_registers
| symbolname
| dereference
"""
p[0] = p[1]
def p_target_registers(p):
"""
target_registers : register
| target_registers ',' register
"""
if len(p) == 2:
p[0] = TargetRegisters([p[1]], _token_sref(p, 1))
else:
p[1].add_register(p[3])
p[0] = p[1]
def p_empty(p):
"""empty :"""
pass

View File

@ -12,6 +12,7 @@ import enum
import builtins
from functools import total_ordering
from typing import Optional, Set, Union, Tuple, Dict, Iterable, Sequence, Any, List, Generator
import attr
PrimitiveType = Union[int, float, str]
@ -74,13 +75,11 @@ class SymbolError(Exception):
_identifier_seq_nr = 0
@attr.s(slots=True, frozen=True)
class SourceRef:
__slots__ = ("file", "line", "column")
def __init__(self, file: str, line: int, column: int=0) -> None:
self.file = file
self.line = line
self.column = column
file = attr.ib(type=str)
line = attr.ib(type=int)
column = attr.ib(type=int, default=0)
def __str__(self) -> str:
if self.column:
@ -89,15 +88,12 @@ class SourceRef:
return "{:s}:{:d}".format(self.file, self.line)
return self.file
def copy(self) -> 'SourceRef':
return SourceRef(self.file, self.line, self.column)
class SymbolDefinition:
def __init__(self, blockname: str, name: str, sourceref: SourceRef, allocate: bool) -> None:
self.blockname = blockname
self.name = name
self.sourceref = sourceref.copy()
self.sourceref = sourceref
self.allocate = allocate # set to false if the variable is memory mapped (or a constant) instead of allocated
global _identifier_seq_nr
self.seq_nr = _identifier_seq_nr
@ -695,9 +691,10 @@ ascii_to_petscii_trans = str.maketrans({
class AstNode:
def __init__(self, sourceref: SourceRef, children: List['AstNode']=None) -> None:
self.sourceref = sourceref.copy()
self.children = children or []
__slots__ = ["sourceref"]
def __init__(self, sourceref: SourceRef) -> None:
self.sourceref = sourceref
@property
def lineref(self) -> str:

View File

@ -1,8 +1,5 @@
; call tests
%output foobar
~ foo {

View File

@ -64,9 +64,8 @@
var .float initfloat4 = 1.70141183e+38
var .float initfloat5 = -1.70141183e+38
var .float initfloat6 = 1.234
var .float (44) zzzz = 333
var .wordarray ( 10 ) uninit_wordarray
var .wordarray( 10 ) uninit_wordarray
var .wordarray(10) init_wordarray = $1234
var .wordarray(10) init_wordarrayb = true
var .array( 10) uninit_bytearray
@ -113,12 +112,12 @@
const .pstext ctext6 = "constant-pstext"
; taking the address of various things:
var .word vmemaddr1 = & membyte1
var .word vmemaddr2 = & memword1
var .word vmemaddr3 = & memfloat
var .word vmemaddr4 = & membytes
var .word vmemaddr5 = & memwords
var .word vmemaddr6 = & memmatrix
var .word vmemaddr1 = &membyte1
var .word vmemaddr2 = &memword1
var .word vmemaddr3 = &memfloat
var .word vmemaddr4 = &membytes
var .word vmemaddr5 = &memwords
var .word vmemaddr6 = &memmatrix
var .word vmemaddr8 = 100*sin(cbyte1)
var .word vmemaddr9 = cword2+$5432
var .word vmemaddr10 = cfloat2b
@ -126,10 +125,10 @@
; taking the address of things from the ZP will work even when it is a var
; because zp-vars get assigned a specific address (from a pool). Also, it's a byte.
var .word initword0a = & ZP.zpmem1
var .word initword0 = & ZP.zpvar1
var initbytea0 = & ZP.zpmem1
var .word initworda1 = & ZP.zpvar1
var .word initword0a = &ZP.zpmem1
var .word initword0 = &ZP.zpvar1
var initbytea0 = &ZP.zpmem1
var .word initworda1 = &ZP.zpvar1
; (constant) expressions
@ -181,7 +180,6 @@ start:
XY = uninitbyte1
XY = "text-immediate"
AY = "text-immediate"
AX = &derp
; AX = &"text-immediate" ; equivalent to simply assigning the string directly
; AX = & "text-immediate" ; equivalent to simply assigning the string directly
AX = ctext3

View File

@ -33,7 +33,7 @@ start: ;foo
Y = X
X = 66
screen = 0
screen = border = cursor = X = Y = A = X = Y = A = border = cursor = border = cursor = 66 ; multi-assign! @todo ply parse
screen = border = cursor = X = Y = A = X = Y = A = border = cursor = border = cursor = 66 ; multi-assign!
border = false
border = true
border = 0
@ -104,6 +104,7 @@ sub customsub (Y)->() {
somelabel1222:
customsub(2)
return

View File

@ -15,11 +15,11 @@ start:
;return
;included_assembly
%asminclude " included.sourcelynx walla derp ", test_include
%asminclude " included.sourcelynx walla derp ", "test_include"
;%asminclude " included.sourcelynx walla derp ", test_include
;%asminclude " included.sourcelynx walla derp ", "test_include"
;included_binary
%asmbinary " included.binary 234 "
%asmbinary " included.binary", $40
;%asmbinary " included.binary 234 "
;%asmbinary " included.binary", $40
%asmbinary "included.binary", $40, $200
}

View File

@ -67,7 +67,7 @@ start:
~ global2 {
make_screen_black:
c64.EXTCOL = c64.BGCOL0 = 0 ; @todo ply parse multiassign
c64.EXTCOL = c64.BGCOL0 = 0
c64.COLOR = 3
Y = true
return

View File

@ -53,7 +53,7 @@ start2:
~ global2 {
make_screen_black:
c64.EXTCOL = c64.BGCOL0 = 0 ; @todo ply parse multi assign
c64.EXTCOL = c64.BGCOL0 = 0
c64.COLOR = 3
return

View File

@ -14,6 +14,7 @@ start:
A += c64.RASTER
A-=c64.TIME_LO
X,A=math.divmod_bytes(A, 99)
A=B=C=foo()
c64scr.print_byte_decimal(A)
c64.CHROUT('\n')
return
@ -27,7 +28,7 @@ rndloop:
tya
sta $0500,x
}
;[wvar1] = 81 ; @todo implement pointers like this
[wvar1] = 81 ; @todo implement pointers like this
goto rndloop