mirror of
https://github.com/irmen/prog8.git
synced 2024-11-22 15:33:02 +00:00
convert bool to 1/0 and single char to byte
This commit is contained in:
parent
afc4ba8ff0
commit
8fc6a5ada9
@ -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()
|
||||
|
@ -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:
|
||||
|
@ -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]++
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
76
todo.ill
76
todo.ill
@ -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
|
||||
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user