mirror of
https://github.com/irmen/prog8.git
synced 2024-11-29 17:50:35 +00:00
tweaks
This commit is contained in:
parent
534bf2f252
commit
614f90fc35
@ -13,7 +13,7 @@ from typing import Optional, Tuple, Set, Dict, Any, no_type_check
|
||||
import attr
|
||||
from .plyparse import parse_file, ParseError, Module, Directive, Block, Subroutine, Scope, VarDef, LiteralValue, \
|
||||
SubCall, Goto, Return, Assignment, InlineAssembly, Register, Expression, ProgramFormat, ZpOptions,\
|
||||
SymbolName, process_constant_expression, process_dynamic_expression
|
||||
SymbolName, Dereference, AddressOf
|
||||
from .plylex import SourceRef, print_bold
|
||||
from .optimize import optimize
|
||||
|
||||
@ -74,7 +74,12 @@ class PlyParser:
|
||||
# process/simplify all expressions (constant folding etc)
|
||||
for block, parent in module.all_scopes():
|
||||
for node in block.nodes:
|
||||
node.process_expressions(block.scope)
|
||||
try:
|
||||
node.process_expressions(block.scope)
|
||||
except ParseError:
|
||||
raise
|
||||
except Exception as x:
|
||||
self.handle_internal_error(x, "process_expressions of node {} in block {}".format(node, block.name))
|
||||
|
||||
@no_type_check
|
||||
def create_multiassigns(self, module: Module) -> None:
|
||||
@ -212,6 +217,10 @@ class PlyParser:
|
||||
self._get_subroutine_usages_from_expression(usages, expr.right, parent_scope)
|
||||
elif isinstance(expr, LiteralValue):
|
||||
return
|
||||
elif isinstance(expr, Dereference):
|
||||
return self._get_subroutine_usages_from_expression(usages, expr.location, parent_scope)
|
||||
elif isinstance(expr, AddressOf):
|
||||
return self._get_subroutine_usages_from_expression(usages, expr.name, parent_scope)
|
||||
elif isinstance(expr, SymbolName):
|
||||
try:
|
||||
symbol = parent_scope[expr.name]
|
||||
@ -357,6 +366,16 @@ class PlyParser:
|
||||
if sys.stderr.isatty():
|
||||
print("\x1b[0m", file=sys.stderr, end="", flush=True)
|
||||
|
||||
def handle_internal_error(self, exc: Exception, msg: str="") -> None:
|
||||
if sys.stderr.isatty():
|
||||
print("\x1b[1m", file=sys.stderr)
|
||||
print("\nERROR: internal parser error: ", exc, file=sys.stderr)
|
||||
if msg:
|
||||
print(" Message:", msg, end="\n\n")
|
||||
if sys.stderr.isatty():
|
||||
print("\x1b[0m", file=sys.stderr, end="", flush=True)
|
||||
raise exc
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
description = "Compiler for IL65 language, code name 'Sick'"
|
||||
|
@ -213,7 +213,7 @@ class AssemblyGenerator:
|
||||
|
||||
def generate_block_init(self, block: Block) -> None:
|
||||
# generate the block initializer
|
||||
# @todo add a block initializer subroutine that can contain custom reset/init code? (static initializers)
|
||||
# @todo add a block initializer subroutine that can contain custom reset/init code? (static initializer)
|
||||
self.p("_il65_init_block\v; (re)set vars to initial values")
|
||||
# @todo optimize init order (sort on value first to avoid needless register loads, etc)
|
||||
self.p("\vlda #0\n\vldx #0")
|
||||
@ -240,6 +240,12 @@ class AssemblyGenerator:
|
||||
float_inits[variable.name] = (vname, fpbytes, vvalue)
|
||||
elif variable.datatype in STRING_DATATYPES:
|
||||
string_inits.append(variable)
|
||||
elif variable.datatype == DataType.BYTEARRAY:
|
||||
pass # @todo init bytearray
|
||||
elif variable.datatype == DataType.WORDARRAY:
|
||||
pass # @todo init wordarray
|
||||
elif variable.datatype == DataType.MATRIX:
|
||||
pass # @todo init matrix
|
||||
else:
|
||||
raise CodeError("weird var datatype", variable.datatype)
|
||||
if float_inits:
|
||||
@ -330,8 +336,8 @@ class AssemblyGenerator:
|
||||
raise CodeError("unknown variable type " + str(vardef.datatype))
|
||||
if string_vars:
|
||||
self.p("il65_string_vars_start")
|
||||
for sv in sorted(string_vars): # must be the same order as in the init routine!!!
|
||||
self.p("{:s}\v.fill {:d}+1\t\t; {}".format(sv.name, len(sv.value), sv.datatype.name.lower()))
|
||||
for svar in sorted(string_vars, key=lambda v: v.name): # must be the same order as in the init routine!!!
|
||||
self.p("{:s}\v.fill {:d}+1\t\t; {}".format(svar.name, len(svar.value), svar.datatype.name.lower()))
|
||||
self.p("")
|
||||
|
||||
def _generate_string_var(self, vardef: VarDef, init: bool=False) -> None:
|
||||
@ -375,8 +381,7 @@ class AssemblyGenerator:
|
||||
assert rvalue is not None
|
||||
if isinstance(rvalue, LiteralValue):
|
||||
rvalue = rvalue.value
|
||||
print("ASSIGN", lvalue, lvalue.datatype, operator, rvalue)
|
||||
# @todo
|
||||
print("ASSIGN", lvalue, lvalue.datatype, operator, rvalue) # @todo
|
||||
|
||||
|
||||
class Assembler64Tass:
|
||||
|
@ -107,7 +107,7 @@ class Optimizer:
|
||||
if block.scope:
|
||||
for stmt in block.scope.filter_nodes(Goto):
|
||||
if isinstance(stmt.condition, Expression):
|
||||
raise NotImplementedError("optimize goto conditionals", stmt.condition) # @todo
|
||||
print("NOT IMPLEMENTED YET: optimize goto conditionals", stmt.condition) # @todo
|
||||
# if cond and isinstance(cond.rvalue, (int, float)) and cond.rvalue.value == 0:
|
||||
# simplified = False
|
||||
# if cond.ifstatus in ("true", "ne"):
|
||||
|
@ -5,6 +5,7 @@ This is the lexer of the IL65 code, that generates a stream of tokens for the pa
|
||||
Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
"""
|
||||
|
||||
import ast
|
||||
import sys
|
||||
import ply.lex
|
||||
import attr
|
||||
@ -258,7 +259,7 @@ def t_STRING(t):
|
||||
(?<!\\) # not preceded by a backslash
|
||||
' # a literal double-quote
|
||||
"""
|
||||
t.value = t.value[1:-1]
|
||||
t.value = ast.literal_eval(t.value)
|
||||
if len(t.value) == 1:
|
||||
t.type = "CHARACTER"
|
||||
if len(t.value) == 2 and t.value[0] == '\\':
|
||||
|
@ -624,31 +624,40 @@ def process_constant_expression(expr: Any, sourceref: SourceRef, symbolscope: Sc
|
||||
if isinstance(value, VarDef):
|
||||
if value.vartype == VarType.MEMORY:
|
||||
return value.value
|
||||
raise ParseError("can't take the address of this {}".format(value.__class__.__name__), expr.name.sourceref)
|
||||
raise ExpressionEvaluationError("taking the address of this {} isn't a constant".format(value.__class__.__name__), expr.name.sourceref)
|
||||
else:
|
||||
raise ExpressionEvaluationError("constant address required, not {}".format(value.__class__.__name__), expr.name.sourceref)
|
||||
except LookupError as x:
|
||||
raise ParseError(str(x), expr.sourceref) from None
|
||||
elif isinstance(expr, SubCall):
|
||||
if isinstance(expr.target, CallTarget):
|
||||
funcname = expr.target.target.name
|
||||
if funcname in math_functions or funcname in builtin_functions:
|
||||
if isinstance(expr.target.target, SymbolName):
|
||||
func_args = []
|
||||
for a in (process_constant_expression(callarg.value, sourceref, symbolscope) for callarg in expr.arguments):
|
||||
if isinstance(a, LiteralValue):
|
||||
func_args.append(a.value)
|
||||
else:
|
||||
func_args.append(a)
|
||||
func = math_functions.get(funcname, builtin_functions.get(funcname))
|
||||
try:
|
||||
return func(*func_args)
|
||||
except Exception as x:
|
||||
raise ExpressionEvaluationError(str(x), expr.sourceref)
|
||||
print("CALLTARGET", expr.target.address_of, expr.target.target) # XXX
|
||||
target = expr.target.target
|
||||
if isinstance(target, SymbolName): # 'function(1,2,3)'
|
||||
funcname = target.name
|
||||
if funcname in math_functions or funcname in builtin_functions:
|
||||
if isinstance(expr.target.target, SymbolName):
|
||||
func_args = []
|
||||
for a in (process_constant_expression(callarg.value, sourceref, symbolscope) for callarg in expr.arguments):
|
||||
if isinstance(a, LiteralValue):
|
||||
func_args.append(a.value)
|
||||
else:
|
||||
func_args.append(a)
|
||||
func = math_functions.get(funcname, builtin_functions.get(funcname))
|
||||
try:
|
||||
return func(*func_args)
|
||||
except Exception as x:
|
||||
raise ExpressionEvaluationError(str(x), expr.sourceref)
|
||||
else:
|
||||
raise ParseError("symbol name required, not {}".format(expr.target.__class__.__name__), expr.sourceref)
|
||||
else:
|
||||
raise ParseError("symbol name required, not {}".format(expr.target.__class__.__name__), expr.sourceref)
|
||||
raise ExpressionEvaluationError("can only use math- or builtin function", expr.sourceref)
|
||||
elif isinstance(target, Dereference): # '[...](1,2,3)'
|
||||
return None # XXX
|
||||
elif isinstance(target, int): # '64738()'
|
||||
return None # XXX
|
||||
else:
|
||||
raise ExpressionEvaluationError("can only use math- or builtin function", expr.sourceref)
|
||||
raise NotImplementedError("weird call target", target) # XXX
|
||||
else:
|
||||
raise ParseError("function name required, not {}".format(expr.target.__class__.__name__), expr.sourceref)
|
||||
elif not isinstance(expr, Expression):
|
||||
@ -707,6 +716,8 @@ def process_dynamic_expression(expr: Any, sourceref: SourceRef, symbolscope: Sco
|
||||
return expr
|
||||
elif isinstance(expr, Register):
|
||||
return expr
|
||||
elif isinstance(expr, Dereference):
|
||||
return expr
|
||||
elif not isinstance(expr, Expression):
|
||||
raise ParseError("expression required, not {}".format(expr.__class__.__name__), expr.sourceref)
|
||||
if expr.unary:
|
||||
|
@ -1,5 +1,6 @@
|
||||
from il65.plylex import lexer, tokens, find_tok_column, literals, reserved, SourceRef
|
||||
from il65.plyparse import parser, TokenFilter, Module, Subroutine, Block, Return, Scope, VarDef, Expression, LiteralValue, Label
|
||||
from il65.plyparse import parser, TokenFilter, Module, Subroutine, Block, Return, Scope, \
|
||||
VarDef, Expression, LiteralValue, Label, SubCall, CallTarget, SymbolName
|
||||
|
||||
|
||||
def test_lexer_definitions():
|
||||
@ -13,7 +14,7 @@ def test_lexer_definitions():
|
||||
assert "if_cc" in reserved
|
||||
|
||||
|
||||
test_source = """ %output prg, sys
|
||||
test_source_1 = """ %output prg, sys
|
||||
|
||||
; c1
|
||||
|
||||
@ -40,8 +41,9 @@ test_source = """ %output prg, sys
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
def test_lexer():
|
||||
lexer.input(test_source)
|
||||
lexer.input(test_source_1)
|
||||
lexer.lineno = 1
|
||||
tokens = list(iter(lexer))
|
||||
token_types = list(t.type for t in tokens)
|
||||
@ -64,8 +66,22 @@ def test_lexer():
|
||||
assert bool_token.value == True
|
||||
|
||||
|
||||
def test_lexer_strings():
|
||||
lexer.input(r"'hello\tbye\n\n' '\n'")
|
||||
lexer.lineno = 1
|
||||
tokens = list(iter(lexer))
|
||||
assert len(tokens) == 2
|
||||
st = tokens[0]
|
||||
assert st.type == "STRING"
|
||||
assert st.value == "hello\tbye\n\n"
|
||||
lexer.input(r"'hello\tbye\n\n'")
|
||||
st = tokens[1]
|
||||
assert st.type == "CHARACTER"
|
||||
assert st.value == '\n'
|
||||
|
||||
|
||||
def test_tokenfilter():
|
||||
lexer.input(test_source)
|
||||
lexer.input(test_source_1)
|
||||
lexer.lineno = 1
|
||||
filter = TokenFilter(lexer)
|
||||
tokens = []
|
||||
@ -88,7 +104,7 @@ def test_parser():
|
||||
lexer.lineno = 1
|
||||
lexer.source_filename = "sourcefile"
|
||||
filter = TokenFilter(lexer)
|
||||
result = parser.parse(input=test_source, tokenfunc=filter.token)
|
||||
result = parser.parse(input=test_source_1, tokenfunc=filter.token)
|
||||
assert isinstance(result, Module)
|
||||
assert result.name == "sourcefile"
|
||||
assert result.scope.name == "<sourcefile global scope>"
|
||||
@ -135,3 +151,32 @@ def test_block_nodes():
|
||||
assert sub2.scope is not None
|
||||
assert len(sub2.scope.nodes) > 0
|
||||
assert sub2.nodes is sub2.scope.nodes
|
||||
|
||||
|
||||
test_source_2 = """
|
||||
~ {
|
||||
999(1,2)
|
||||
&zz()
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
def test_parser_2():
|
||||
lexer.lineno = 1
|
||||
lexer.source_filename = "sourcefile"
|
||||
filter = TokenFilter(lexer)
|
||||
result = parser.parse(input=test_source_2, tokenfunc=filter.token)
|
||||
block = result.nodes[0]
|
||||
call = block.nodes[0]
|
||||
assert isinstance(call, SubCall)
|
||||
assert len(call.arguments) == 2
|
||||
assert isinstance(call.target, CallTarget)
|
||||
assert call.target.target == 999
|
||||
assert call.target.address_of is False
|
||||
call = block.nodes[1]
|
||||
assert isinstance(call, SubCall)
|
||||
assert len(call.arguments) == 0
|
||||
assert isinstance(call.target, CallTarget)
|
||||
assert isinstance(call.target.target, SymbolName)
|
||||
assert call.target.target.name == "zz"
|
||||
assert call.target.address_of is True
|
||||
|
@ -50,6 +50,10 @@ bar: goto $c000
|
||||
; goto [AX()] % ; @todo error, must be return()
|
||||
goto [var1]
|
||||
goto [mem1] ; comment
|
||||
goto $c000
|
||||
goto 64738
|
||||
64738(1,2) ; @todo jsr $64738 ??
|
||||
return 9999() ; @todo jsr 9999 ??
|
||||
return [AX]()
|
||||
return [var1] () ; comment
|
||||
return [mem1] ()
|
||||
|
@ -1,4 +1,4 @@
|
||||
%output prg,basic
|
||||
%output basic
|
||||
|
||||
%import c64lib
|
||||
|
||||
|
@ -125,7 +125,7 @@
|
||||
; 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 initword0a = &ZP.zpmem1 ; @todo should work, reference this symbols' generated address (@todo generate address for ZP)
|
||||
var .word initword0 = &ZP.zpvar1
|
||||
var initbytea0 = &ZP.zpmem1
|
||||
var .word initworda1 = &ZP.zpvar1
|
||||
|
@ -1,4 +1,4 @@
|
||||
%output prg,basic
|
||||
%output basic
|
||||
|
||||
%import c64lib
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
; var definitions and immediate primitive data type tests
|
||||
|
||||
%output prg, basic
|
||||
%output basic
|
||||
%zp clobber
|
||||
|
||||
%import c64lib
|
||||
|
@ -1,4 +1,4 @@
|
||||
%output prg,basic
|
||||
%output basic
|
||||
%import c64lib
|
||||
%import mathlib
|
||||
|
||||
@ -95,4 +95,4 @@ sub goodbye ()->() {
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
; line 3 comment
|
||||
|
||||
|
||||
%output basic , prg ; create a c-64 program with basic SYS call to launch it
|
||||
%output basic ; create a c-64 program with basic SYS call to launch it
|
||||
%zp restore , clobber ; clobber over the zp memory normally used by basic/kernel rom, frees up more zp
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
%output prg,basic ; create a c-64 program with basic SYS call to launch it
|
||||
%output basic ; create a c-64 program with basic SYS call to launch it
|
||||
|
||||
|
||||
%import c64lib ; searched in several locations and with .ill file extension added
|
||||
|
@ -1,4 +1,4 @@
|
||||
%output prg,basic ; create a c-64 program with basic SYS to() launch it
|
||||
%output basic ; create a c-64 program with basic SYS to() launch it
|
||||
|
||||
%import "c64lib.ill"
|
||||
|
||||
|
35
todo.ill
35
todo.ill
@ -1,29 +1,36 @@
|
||||
%output basic
|
||||
|
||||
~ ZP {
|
||||
var zp1_1
|
||||
var zp1_2
|
||||
var zp1_3
|
||||
var zp1_4
|
||||
const zpc1_1
|
||||
const zpc1_2
|
||||
var zp1_1 = 200
|
||||
var zp1_2 = 200
|
||||
var .word zp1_3 = $ff99
|
||||
var .word zp1_4 = $ee88
|
||||
const zpc1_1 = 55
|
||||
const .word zpc1_2 = 2333.54566
|
||||
const .float zpc1_3 = 6.54566
|
||||
}
|
||||
|
||||
~ ZP {
|
||||
var zp2_1
|
||||
var zp2_2
|
||||
var zp2_3
|
||||
var zp2_4
|
||||
const zpc2_1
|
||||
const zpc2_2
|
||||
var zp2_1 = 100
|
||||
var zp2_2 = 100
|
||||
var .word zp2_3 = $55aa
|
||||
var .word zp2_4 = $66bb
|
||||
const .text zpc2_1 = "hello"
|
||||
const zpc2_2 = 0
|
||||
}
|
||||
|
||||
|
||||
~ main {
|
||||
~ main {
|
||||
|
||||
var .text hello_str = "hello there"
|
||||
var .float float1 = 3.14
|
||||
var .float float2 = 3.14
|
||||
var .float float3 = 3.14
|
||||
var .float float4 = 3.14
|
||||
var .float float5 = 3.14
|
||||
|
||||
start:
|
||||
|
||||
return
|
||||
return 1.22, 46
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user