convert bool to 1/0 and single char to byte

This commit is contained in:
Irmen de Jong 2018-01-15 02:22:14 +01:00
parent afc4ba8ff0
commit 8fc6a5ada9
10 changed files with 164 additions and 89 deletions

View File

@ -15,7 +15,6 @@ from .plyparse import parse_file, ParseError, Module, Directive, Block, Subrouti
SubCall, Goto, Return, Assignment, InlineAssembly, Register, Expression, ProgramFormat, ZpOptions,\
SymbolName, Dereference, AddressOf, IncrDecr, Label, AstNode, datatype_of, coerce_constant_value
from .plylex import SourceRef, print_bold
from .optimize import optimize
from .datatypes import DataType, VarType
@ -212,7 +211,7 @@ class PlyParser:
else:
raise ParseError("invalid directive args", directive.sourceref)
elif directive.name == "address":
if len(directive.args) != 1 or not isinstance(directive.args[0], int):
if len(directive.args) != 1 or type(directive.args[0]) is not int:
raise ParseError("expected one integer directive argument", directive.sourceref)
if block.format == ProgramFormat.BASIC:
raise ParseError("basic cannot have a custom load address", directive.sourceref)
@ -533,12 +532,3 @@ class Zeropage:
def available(self) -> int:
return len(self.free)
if __name__ == "__main__":
description = "Compiler for IL65 language, code name 'Sick'"
print("\n" + description + "\n")
plyparser = PlyParser()
m = plyparser.parse_file(sys.argv[1])
optimize(m)
print()

View File

@ -22,6 +22,7 @@ def to_hex(number: int) -> str:
# 0..15 -> "0".."15"
# 16..255 -> "$10".."$ff"
# 256..65536 -> "$0100".."$ffff"
assert type(number) is int
if number is None:
raise ValueError("number")
if 0 <= number < 16:

View File

@ -209,4 +209,4 @@ def generate_incrdecr(out: Callable, stmt: IncrDecr, scope: Scope) -> None:
raise CodeError("cannot in/decrement memory of type " + str(target.datatype), stmt.howmuch)
else:
raise CodeError("cannot in/decrement", target)
raise CodeError("cannot in/decrement", target) # @todo support more such as [dereference]++

View File

@ -15,6 +15,7 @@ from . import to_hex, to_mflpt5, CodeError
def generate_block_init(out: Callable, block: Block) -> None:
# generate the block initializer
# @todo add a block initializer subroutine that can contain custom reset/init code? (static initializer)
# @todo will be called at program start automatically, so there's no risk of forgetting to call it manually
def _memset(varname: str, value: int, size: int) -> None:
if size > 6:
@ -59,13 +60,13 @@ def generate_block_init(out: Callable, block: Block) -> None:
if vardef.vartype == VarType.VAR:
vars_by_datatype[vardef.datatype].append(vardef)
for bytevar in sorted(vars_by_datatype[DataType.BYTE], key=lambda vd: vd.value):
assert isinstance(bytevar.value, int)
assert type(bytevar.value) is int
if bytevar.value != prev_value_a:
out("\vlda #${:02x}".format(bytevar.value))
prev_value_a = bytevar.value
out("\vsta {:s}".format(bytevar.name))
for wordvar in sorted(vars_by_datatype[DataType.WORD], key=lambda vd: vd.value):
assert isinstance(wordvar.value, int)
assert type(wordvar.value) is int
v_hi, v_lo = divmod(wordvar.value, 256)
if v_hi != prev_value_a:
out("\vlda #${:02x}".format(v_hi))
@ -80,13 +81,13 @@ def generate_block_init(out: Callable, block: Block) -> None:
fpbytes = to_mflpt5(floatvar.value) # type: ignore
float_inits[floatvar.name] = (floatvar.name, fpbytes, floatvar.value)
for arrayvar in vars_by_datatype[DataType.BYTEARRAY]:
assert isinstance(arrayvar.value, int)
assert type(arrayvar.value) is int
_memset(arrayvar.name, arrayvar.value, arrayvar.size[0])
for arrayvar in vars_by_datatype[DataType.WORDARRAY]:
assert isinstance(arrayvar.value, int)
assert type(arrayvar.value) is int
_memsetw(arrayvar.name, arrayvar.value, arrayvar.size[0])
for arrayvar in vars_by_datatype[DataType.MATRIX]:
assert isinstance(arrayvar.value, int)
assert type(arrayvar.value) is int
_memset(arrayvar.name, arrayvar.value, arrayvar.size[0] * arrayvar.size[1])
if float_inits:
out("\vldx #4")
@ -248,7 +249,7 @@ def _format_string(value: str, screencodes: bool = False) -> str:
def _numeric_value_str(value: Any, as_hex: bool=False) -> str:
if isinstance(value, bool):
return "1" if value else "0"
if isinstance(value, int):
if type(value) is int:
if as_hex:
return to_hex(value)
return str(value)

View File

@ -23,6 +23,7 @@ class Optimizer:
self.remove_unused_subroutines()
self.optimize_compare_with_zero()
# @todo join multiple incr/decr of same var into one (if value stays < 256)
# @todo analyse for unreachable code and remove that (f.i. code after goto or return that has no label so can never be jumped to)
self.remove_empty_blocks()
def optimize_assignments(self):

View File

@ -530,9 +530,6 @@ class Dereference(AstNode):
class LiteralValue(AstNode):
value = attr.ib()
def __repr__(self) -> str:
return repr(self.value)
@attr.s(cmp=False, repr=False)
class AddressOf(AstNode):
@ -645,12 +642,8 @@ def coerce_constant_value(datatype: DataType, value: Union[int, float, str],
raise OverflowError("value out of range for word: " + str(value))
if datatype == DataType.FLOAT and not (FLOAT_MAX_NEGATIVE <= value <= FLOAT_MAX_POSITIVE): # type: ignore
raise OverflowError("value out of range for float: " + str(value))
if datatype in (DataType.BYTE, DataType.WORD, DataType.FLOAT):
if not isinstance(value, (int, float)):
raise TypeError("cannot assign '{:s}' to {:s}".format(type(value).__name__, datatype.name.lower()))
if datatype in (DataType.BYTE, DataType.BYTEARRAY, DataType.MATRIX) and isinstance(value, str):
if len(value) == 1:
return True, char_to_bytevalue(value)
if isinstance(value, str) and len(value) == 1 and (datatype.isnumeric() or datatype.isarray()):
return True, char_to_bytevalue(value)
# if we're an integer value and the passed value is float, truncate it (and give a warning)
if datatype in (DataType.BYTE, DataType.WORD, DataType.MATRIX) and isinstance(value, float):
frac = math.modf(value)
@ -661,6 +654,14 @@ def coerce_constant_value(datatype: DataType, value: Union[int, float, str],
return True, value
if isinstance(value, (int, float)):
verify_bounds(value)
if isinstance(value, (Expression, SubCall)):
return False, value
elif datatype == DataType.WORD:
if not isinstance(value, (int, float, str, Dereference, Register, SymbolName, AddressOf)):
raise TypeError("cannot assign '{:s}' to {:s}".format(type(value).__name__, datatype.name.lower()), sourceref)
elif datatype in (DataType.BYTE, DataType.WORD, DataType.FLOAT):
if not isinstance(value, (int, float, Dereference, Register, SymbolName)):
raise TypeError("cannot assign '{:s}' to {:s}".format(type(value).__name__, datatype.name.lower()), sourceref)
return False, value
@ -737,7 +738,7 @@ def process_constant_expression(expr: Any, sourceref: SourceRef, symbolscope: Sc
raise ExpressionEvaluationError("can only use math- or builtin function", expr.sourceref)
elif isinstance(target, Dereference): # '[...](1,2,3)'
raise ExpressionEvaluationError("dereferenced value call is not a constant value", expr.sourceref)
elif isinstance(target, int): # '64738()'
elif type(target) is int: # '64738()'
raise ExpressionEvaluationError("immediate address call is not a constant value", expr.sourceref)
else:
raise NotImplementedError("weird call target", target)
@ -1028,6 +1029,11 @@ def p_literal_value(p):
| STRING
| CHARACTER
| BOOLEAN"""
tok = p.slice[-1]
if tok.type == "CHARACTER":
p[1] = char_to_bytevalue(p[1]) # character literals are converted to byte value.
elif tok.type == "BOOLEAN":
p[1] = int(p[1]) # boolean literals are converted to integer form (true=1, false=0).
p[0] = LiteralValue(value=p[1], sourceref=_token_sref(p, 1))
@ -1038,7 +1044,7 @@ def p_subroutine(p):
body = p[10]
if isinstance(body, Scope):
p[0] = Subroutine(name=p[2], param_spec=p[4] or [], result_spec=p[8] or [], scope=body, sourceref=_token_sref(p, 1))
elif isinstance(body, int):
elif type(body) is int:
p[0] = Subroutine(name=p[2], param_spec=p[4] or [], result_spec=p[8] or [], address=body, sourceref=_token_sref(p, 1))
else:
raise TypeError("subroutine_body", p.slice)

View File

@ -1,5 +1,6 @@
import pytest
from il65 import datatypes
from il65.plyparse import coerce_constant_value
from il65.compile import ParseError
from il65.plylex import SourceRef
from il65.emit import to_hex, to_mflpt5
@ -100,37 +101,40 @@ def test_char_to_bytevalue():
def test_coerce_value():
assert datatypes.coerce_value(datatypes.DataType.BYTE, 0) == (False, 0)
assert datatypes.coerce_value(datatypes.DataType.BYTE, 255) == (False, 255)
assert datatypes.coerce_value(datatypes.DataType.WORD, 0) == (False, 0)
assert datatypes.coerce_value(datatypes.DataType.WORD, 65535) == (False, 65535)
assert datatypes.coerce_value(datatypes.DataType.FLOAT, -999.22) == (False, -999.22)
assert datatypes.coerce_value(datatypes.DataType.FLOAT, 123.45) == (False, 123.45)
assert datatypes.coerce_value(datatypes.DataType.BYTE, 5.678) == (True, 5)
assert datatypes.coerce_value(datatypes.DataType.WORD, 5.678) == (True, 5)
assert datatypes.coerce_value(datatypes.DataType.STRING, "string") == (False, "string")
assert datatypes.coerce_value(datatypes.DataType.STRING_P, "string") == (False, "string")
assert datatypes.coerce_value(datatypes.DataType.STRING_S, "string") == (False, "string")
assert datatypes.coerce_value(datatypes.DataType.STRING_PS, "string") == (False, "string")
assert coerce_constant_value(datatypes.DataType.BYTE, 0) == (False, 0)
assert coerce_constant_value(datatypes.DataType.BYTE, 255) == (False, 255)
assert coerce_constant_value(datatypes.DataType.BYTE, '@') == (True, 64)
assert coerce_constant_value(datatypes.DataType.WORD, 0) == (False, 0)
assert coerce_constant_value(datatypes.DataType.WORD, 65535) == (False, 65535)
assert coerce_constant_value(datatypes.DataType.WORD, '@') == (True, 64)
assert coerce_constant_value(datatypes.DataType.FLOAT, -999.22) == (False, -999.22)
assert coerce_constant_value(datatypes.DataType.FLOAT, 123.45) == (False, 123.45)
assert coerce_constant_value(datatypes.DataType.FLOAT, '@') == (True, 64)
assert coerce_constant_value(datatypes.DataType.BYTE, 5.678) == (True, 5)
assert coerce_constant_value(datatypes.DataType.WORD, 5.678) == (True, 5)
assert coerce_constant_value(datatypes.DataType.STRING, "string") == (False, "string")
assert coerce_constant_value(datatypes.DataType.STRING_P, "string") == (False, "string")
assert coerce_constant_value(datatypes.DataType.STRING_S, "string") == (False, "string")
assert coerce_constant_value(datatypes.DataType.STRING_PS, "string") == (False, "string")
with pytest.raises(OverflowError):
datatypes.coerce_value(datatypes.DataType.BYTE, -1)
coerce_constant_value(datatypes.DataType.BYTE, -1)
with pytest.raises(OverflowError):
datatypes.coerce_value(datatypes.DataType.BYTE, 256)
coerce_constant_value(datatypes.DataType.BYTE, 256)
with pytest.raises(OverflowError):
datatypes.coerce_value(datatypes.DataType.BYTE, 256.12345)
coerce_constant_value(datatypes.DataType.BYTE, 256.12345)
with pytest.raises(OverflowError):
datatypes.coerce_value(datatypes.DataType.WORD, -1)
coerce_constant_value(datatypes.DataType.WORD, -1)
with pytest.raises(OverflowError):
datatypes.coerce_value(datatypes.DataType.WORD, 65536)
coerce_constant_value(datatypes.DataType.WORD, 65536)
with pytest.raises(OverflowError):
datatypes.coerce_value(datatypes.DataType.WORD, 65536.12345)
coerce_constant_value(datatypes.DataType.WORD, 65536.12345)
with pytest.raises(OverflowError):
datatypes.coerce_value(datatypes.DataType.FLOAT, -1.7014118346e+38)
coerce_constant_value(datatypes.DataType.FLOAT, -1.7014118346e+38)
with pytest.raises(OverflowError):
datatypes.coerce_value(datatypes.DataType.FLOAT, 1.7014118347e+38)
coerce_constant_value(datatypes.DataType.FLOAT, 1.7014118347e+38)
with pytest.raises(TypeError):
datatypes.coerce_value(datatypes.DataType.BYTE, "string")
coerce_constant_value(datatypes.DataType.BYTE, "string")
with pytest.raises(TypeError):
datatypes.coerce_value(datatypes.DataType.WORD, "string")
coerce_constant_value(datatypes.DataType.WORD, "string")
with pytest.raises(TypeError):
datatypes.coerce_value(datatypes.DataType.FLOAT, "string")
coerce_constant_value(datatypes.DataType.FLOAT, "string")

View File

@ -1,7 +1,7 @@
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, SubCall, CallTarget, SymbolName, Dereference
from il65.datatypes import DataType
from il65.datatypes import DataType, char_to_bytevalue
def lexer_error(sourceref: SourceRef, fmtstring: str, *args: str) -> None:
@ -128,8 +128,8 @@ def test_parser():
assert isinstance(bool_vdef, VarDef)
assert isinstance(bool_vdef.value, Expression)
assert isinstance(bool_vdef.value.right, LiteralValue)
assert isinstance(bool_vdef.value.right.value, bool)
assert bool_vdef.value.right.value == True
assert isinstance(bool_vdef.value.right.value, int)
assert bool_vdef.value.right.value == 1
assert block.address == 49152
sub2 = block.scope["calculate"]
assert sub2 is sub
@ -233,3 +233,54 @@ def test_typespec():
assert t2.size is None
assert t3.size is None
assert t4.size is None
test_source_4 = """
~ {
var x1 = '@'
var x2 = 'π'
var x3 = 'abc'
A = '@'
A = 'π'
A = 'abc'
}
"""
def test_char_string():
lexer.lineno = 1
lexer.source_filename = "sourcefile"
filter = TokenFilter(lexer)
result = parser.parse(input=test_source_4, tokenfunc=filter.token)
nodes = result.nodes[0].nodes
var1, var2, var3, assgn1, assgn2, assgn3, = nodes
assert var1.value.value == 64
assert var2.value.value == 126
assert var3.value.value == "abc"
assert assgn1.right.value == 64
assert assgn2.right.value == 126
assert assgn3.right.value == "abc"
test_source_5 = """
~ {
var x1 = true
var x2 = false
A = true
A = false
}
"""
def test_boolean_int():
lexer.lineno = 1
lexer.source_filename = "sourcefile"
filter = TokenFilter(lexer)
result = parser.parse(input=test_source_5, tokenfunc=filter.token)
nodes = result.nodes[0].nodes
var1, var2, assgn1, assgn2, = nodes
assert type(var1.value.value) is int and var1.value.value == 1
assert type(var2.value.value) is int and var2.value.value == 0
assert type(assgn1.right.value) is int and assgn1.right.value == 1
assert type(assgn2.right.value) is int and assgn2.right.value == 0

View File

@ -85,7 +85,16 @@ game_over:
c64scr.print_string("\nToo bad! It was: ")
c64scr.print_byte_decimal(secretnumber)
c64.CHROUT('\n')
goodbye()
goodbye() ; @todo fix subroutine usage tracking, it doesn't register this one
return
return
return
return
return
return
return
return
return
return
sub goodbye ()->() {
@ -93,6 +102,8 @@ sub goodbye ()->() {
memory y = $c000 ; @todo vars in sub
const q = 22 ; @todo const in sub
xxxxxx++
y++
xxxxxx = q *4
xxxxxx = qqqqq *44 ;@todo symbol error

View File

@ -4,30 +4,58 @@
~ main {
var zp1_1 = 200
var zp1_2 = 200
var .float zpf1
var .text zp_s1 = "hello\n"
var .ptext zp_s2 = "goodbye\n"
var .stext zp_s3 = "welcome\n"
var .pstext zp_s4 = "go away\n"
const .text ctext = "constant\n"
var .byte v1t = true
var .byte v1f = false
var .word v2t = true
var .word v2f = false
var .float v3t = true
var .float v3f = false
var .text v4t = true
var .text v4f = false
var .array(3) v5t = true
var .array(3) v5f = false
var .array(10) v6t = true
var .array(10) v6f = false
var .wordarray(3) v7t = true
var .wordarray(3) v7f = false
var .wordarray(10) v8t = true
var .wordarray(10) v8f = false
var .matrix(2,2) v9t = true
var .matrix(2,2) v9f = false
var .matrix(5,5) v10t = true
var .matrix(5,5) v10f = false
const .byte c1t=true
const .byte c1f=false
const .word c2t=true
const .word c2f=false
const .float c3t=true
const .float c3f=false
var .array(20) arr1 = $ea
var .wordarray(20) arr2 = $ea
memory border = $d020
const .word cword = 5/3
start:
%breakpoint abc,def
;c3f=444 ; @todo constant error
;c3f=c3f ; @todo constant error
;c3f=c2t ; @todo constant error
;c3f+=2.23424 ; @todo constant error
v3t++
v3t+=1
v3t+=0
;v3t+=2.23424 ; @todo store as constant float with generated name, replace value node
;v3t+=2.23424 ; @todo store as constant float with generated name, replace value node
;v3t+=2.23425 ; @todo store as constant float with generated name, replace value node
; v4t++ ; @todo parser error
; v4t+=20 ; @todo parser error
; v4t+=2000 ; @todo parser error
; v5t++ ; @todo parser error
; v5t+=20 ; @todo parser error
; v5t+=2000 ; @todo parser error
A++
X--
A+=1
X-=2
border++
zp1_1++
zpf1++
;[AX]++
;[AX .byte]++
;[AX .word]++
@ -42,25 +70,7 @@ start:
XY+=222
A=222/13 ; @todo warn truncate (in assignment stmt)
XY+=666
zpf1+=1
zpf1+=2
zpf1+=2.123425425 ; @todo store as constant float with generated name, replace value node
foobar()
return 44
sub foobar () -> () {
return
%breakpoint yep
return
}
label2:
A++
%noreturn
}