From dd41f2d969cb42b04123827b617d25d84b81d3ff Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Fri, 10 Apr 2020 13:22:23 +0200 Subject: [PATCH 01/14] Fix typo --- symbols/paramlist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/symbols/paramlist.py b/symbols/paramlist.py index cf80a3b1b..4f494d0ac 100644 --- a/symbols/paramlist.py +++ b/symbols/paramlist.py @@ -34,7 +34,7 @@ def make_node(cls, node, *params): (declared in a function declaration) Parameters: -node: A SymbolPARAMLIST instance or None - -params: SymbolPARAMDECL insances + -params: SymbolPARAMDECL instances """ if node is None: node = cls() From 4a9ef0cdb78cba0eff73e55d158469c751a21890 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Fri, 10 Apr 2020 13:26:43 +0200 Subject: [PATCH 02/14] Add an InternalError exception class --- api/errors.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/api/errors.py b/api/errors.py index bf8ad6701..8977b0701 100644 --- a/api/errors.py +++ b/api/errors.py @@ -43,3 +43,8 @@ def __init__(self, symbol): class InvalidBuiltinFunctionError(Error): def __init__(self, fname): self.msg = "Invalid BUILTIN function '%s'" % fname + + +class InternalError: + def __init__(self, msg): + self.msg = msg From 416a06578a9cc97842fa463e039cd1ebddb3c56d Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Fri, 10 Apr 2020 13:27:09 +0200 Subject: [PATCH 03/14] Add error message for arrays Cannot pass an Array byval --- api/errmsg.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/api/errmsg.py b/api/errmsg.py index 319ec8821..ea62696e0 100644 --- a/api/errmsg.py +++ b/api/errmsg.py @@ -194,3 +194,10 @@ def syntax_error_cannot_assign_not_a_var(lineno, id_): # ---------------------------------------- def syntax_error_address_must_be_constant(lineno): syntax_error(lineno, 'Address must be a numeric constant expression') + + +# ---------------------------------------- +# Cannot pass an array by value +# ---------------------------------------- +def syntax_error_cannot_pass_array_by_value(lineno, id_): + syntax_error(lineno, "Array parameter '%s' must be passed ByRef" % id_) From 6e26e280b72f750624dc798c1825b22009e8fee2 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Fri, 10 Apr 2020 13:32:30 +0200 Subject: [PATCH 04/14] Allow declaring array parameters byVal This commit only affects the Symbol Table. The parser still has to allow the syntax and use this method. --- api/symboltable.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/api/symboltable.py b/api/symboltable.py index 95bf526e7..19e35d3f2 100644 --- a/api/symboltable.py +++ b/api/symboltable.py @@ -685,7 +685,7 @@ def declare_label(self, id_, lineno): entry.type_ = self.basic_types[global_.PTR_TYPE] return entry - def declare_param(self, id_, lineno, type_=None): + def declare_param(self, id_, lineno, type_=None, is_array=False): """ Declares a parameter Check if entry.declared is False. Otherwise raises an error. """ @@ -693,9 +693,16 @@ def declare_param(self, id_, lineno, type_=None): scope=self.current_scope, show_error=True): return None - entry = self.declare(id_, lineno, symbols.PARAMDECL(id_, lineno, type_)) + if is_array: + entry = self.declare(id_, lineno, symbols.VARARRAY(id_, symbols.BOUNDLIST(), lineno, None, type_)) + entry.callable = True + entry.scope = SCOPE.parameter + else: + entry = self.declare(id_, lineno, symbols.PARAMDECL(id_, lineno, type_)) + if entry is None: return + entry.declared = True if entry.type_.implicit: warning_implicit_type(lineno, id_, type_) From c9ab4eba5446d11925c07396643070575a06caaa Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Fri, 10 Apr 2020 13:33:44 +0200 Subject: [PATCH 05/14] Ensures all items in arglist are arguments Also fixes a typo. --- symbols/call.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/symbols/call.py b/symbols/call.py index 7bdef0252..98223933a 100644 --- a/symbols/call.py +++ b/symbols/call.py @@ -17,6 +17,7 @@ from .symbol_ import Symbol from .function import SymbolFUNCTION from .arglist import SymbolARGLIST +from .argument import SymbolARGUMENT from .var import SymbolVAR from .type_ import Type @@ -28,13 +29,14 @@ class SymbolCALL(Symbol): Parameters: id_: The symbol table entry - arglist: a SymbolArglist instance + arglist: a SymbolARGLIST instance lineno: source code line where this call was made """ def __init__(self, entry, arglist, lineno): super(SymbolCALL, self).__init__() assert isinstance(lineno, int) + assert all(isinstance(x, SymbolARGUMENT) for x in arglist) self.entry = entry self.args = arglist # Func. call / array access self.lineno = lineno From d164af29c19bb4d8bdfffbe44127df8a692cb9de Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Fri, 10 Apr 2020 13:34:22 +0200 Subject: [PATCH 06/14] Ensures all array indexes have the right type Also fixes a typo. --- symbols/arrayaccess.py | 44 ++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/symbols/arrayaccess.py b/symbols/arrayaccess.py index f3e968db6..d378423a0 100644 --- a/symbols/arrayaccess.py +++ b/symbols/arrayaccess.py @@ -14,6 +14,7 @@ from api.errmsg import warning from api.check import is_number from api.check import is_const +from api.constants import SCOPE from .call import SymbolCALL from .number import SymbolNUMBER as NUMBER @@ -33,11 +34,12 @@ class SymbolARRAYACCESS(SymbolCALL): it only returns the pointer address to the element). Parameters: - entry will be the symboltable entry. + entry will be the symbol table entry. Arglist a SymbolARGLIST instance. """ def __init__(self, entry, arglist, lineno): super(SymbolARRAYACCESS, self).__init__(entry, arglist, lineno) + assert all(gl.BOUND_TYPE == x.type_.type_ for x in arglist), "Invalid type for array index" @property def entry(self): @@ -77,6 +79,9 @@ def offset(self): Otherwise, if it's not constant (e.g. A(i)) returns None """ + if self.scope == SCOPE.parameter: + return None + offset = 0 # Now we must typecast each argument to a u16 (POINTER) type # i is the dimension ith index, b is the bound @@ -103,24 +108,25 @@ def make_node(cls, id_, arglist, lineno): if variable is None: return None - if len(variable.bounds) != len(arglist): - syntax_error(lineno, "Array '%s' has %i dimensions, not %i" % - (variable.name, len(variable.bounds), len(arglist))) - return None - - # Checks for array subscript range if the subscript is constant - # e.g. A(1) is a constant subscript access - for i, b in zip(arglist, variable.bounds): - btype = gl.SYMBOL_TABLE.basic_types[gl.BOUND_TYPE] - lower_bound = NUMBER(b.lower, type_=btype, lineno=lineno) - i.value = BINARY.make_node('MINUS', - TYPECAST.make_node(btype, i.value, lineno), - lower_bound, lineno, func=lambda x, y: x - y, - type_=btype) - if is_number(i.value) or is_const(i.value): - val = i.value.value - if val < 0 or val > b.count: - warning(lineno, "Array '%s' subscript out of range" % id_) + if variable.scope != SCOPE.parameter: + if len(variable.bounds) != len(arglist): + syntax_error(lineno, "Array '%s' has %i dimensions, not %i" % + (variable.name, len(variable.bounds), len(arglist))) + return None + + # Checks for array subscript range if the subscript is constant + # e.g. A(1) is a constant subscript access + for i, b in zip(arglist, variable.bounds): + btype = gl.SYMBOL_TABLE.basic_types[gl.BOUND_TYPE] + lower_bound = NUMBER(b.lower, type_=btype, lineno=lineno) + i.value = BINARY.make_node('MINUS', + TYPECAST.make_node(btype, i.value, lineno), + lower_bound, lineno, func=lambda x, y: x - y, + type_=btype) + if is_number(i.value) or is_const(i.value): + val = i.value.value + if val < 0 or val > b.count: + warning(lineno, "Array '%s' subscript out of range" % id_) # Returns the variable entry and the node return cls(variable, arglist, lineno) From 038fa962be5a37b0c83585e29ddfcedc7aafc1c6 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Fri, 10 Apr 2020 13:36:00 +0200 Subject: [PATCH 07/14] Return 2 byte size for ByRef arrays The size of an array passed by ref in the stack is the size of it's pointer (2 bytes for Z80) --- symbols/vararray.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/symbols/vararray.py b/symbols/vararray.py index 5147b2707..9377224a2 100644 --- a/symbols/vararray.py +++ b/symbols/vararray.py @@ -14,6 +14,7 @@ import api.global_ as gl from api.constants import TYPE from api.constants import CLASS +from api.constants import SCOPE from .var import SymbolVAR from .boundlist import SymbolBOUNDLIST @@ -42,7 +43,7 @@ def count(self): @property def size(self): - return self.count * self.type_.size + return self.count * self.type_.size if self.scope != SCOPE.parameter else TYPE.size(gl.PTR_TYPE) @property def memsize(self): From 1ba939bfbc13456c17b0fe8bfa995b2ef5a8613f Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Fri, 10 Apr 2020 13:38:23 +0200 Subject: [PATCH 08/14] Allow passing arrays as parameters * Arrays will be passed always by Ref * Uses the same syntax as in FreeBasic * Allows different array-bounds to use the same function Still some extra checks pending --- arch/zx48k/translator.py | 12 ++-- zxbparser.py | 145 +++++++++++++++++++++++++++------------ 2 files changed, 110 insertions(+), 47 deletions(-) diff --git a/arch/zx48k/translator.py b/arch/zx48k/translator.py index cad2c1f47..89f66d940 100644 --- a/arch/zx48k/translator.py +++ b/arch/zx48k/translator.py @@ -20,6 +20,7 @@ from api.errors import InvalidLoopError from api.errors import InvalidOperatorError from api.errors import InvalidBuiltinFunctionError +from api.errors import InternalError from . import backend from .backend.__float import _float @@ -300,17 +301,18 @@ def visit_LETARRAY(self, node): if scope == SCOPE.global_: self.ic_astore(arr.type_, arr.entry.mangled, node.children[1].t) elif scope == SCOPE.parameter: - self.ic_pastore(arr.type_, arr.entry.offset, node.children[1].t) + # HINT: Arrays are always passed ByREF + self.ic_pastore(arr.type_, '*{}'.format(arr.entry.offset), node.children[1].t) elif scope == SCOPE.local: self.ic_pastore(arr.type_, -arr.entry.offset, node.children[1].t) else: name = arr.entry.data_label if scope == SCOPE.global_: self.ic_store(arr.type_, '%s + %i' % (name, arr.offset), node.children[1].t) - elif scope == SCOPE.parameter: - self.ic_pstore(arr.type_, arr.entry.offset - arr.offset, node.children[1].t) elif scope == SCOPE.local: self.ic_pstore(arr.type_, -(arr.entry.offset - arr.offset), node.children[1].t) + else: + raise InternalError("Invalid scope {} for variable '{}'".format(scope, arr.entry.name)) def visit_LETSUBSTR(self, node): yield node.children[3] @@ -1345,12 +1347,12 @@ def visit_FUNCTION(self, node): # if self.O_LEVEL > 1: # return - if local_var.class_ == CLASS.array and local_var.scope != SCOPE.global_: + if local_var.class_ == CLASS.array and local_var.scope == SCOPE.local: l = [len(local_var.bounds) - 1] + [x.count for x in local_var.bounds[1:]] # TODO Check this q = [] for x in l: q.append('%02X' % (x & 0xFF)) - q.append('%02X' % (x >> 8)) + q.append('%02X' % ((x & 0xFF) >> 8)) q.append('%02X' % local_var.type_.size) r = [] diff --git a/zxbparser.py b/zxbparser.py index 090dfa929..e2572113b 100755 --- a/zxbparser.py +++ b/zxbparser.py @@ -294,6 +294,9 @@ def make_array_access(id_, lineno, arglist): """ Creates an array access. A(x1, x2, ..., xn). This is an RVALUE (Read the element) """ + for i, arg in enumerate(arglist): + arg.value = make_typecast(TYPE.by_name(api.constants.TYPE.to_string(gl.BOUND_TYPE)), arg.value, arg.lineno) + return symbols.ARRAYACCESS.make_node(id_, arglist, lineno) @@ -379,10 +382,10 @@ def make_call(id_, lineno, args): return make_func_call(id_, lineno, args) -def make_param_decl(id_, lineno, typedef): +def make_param_decl(id_, lineno, typedef, is_array=False): """ Wrapper that creates a param declaration """ - return SYMBOL_TABLE.declare_param(id_, lineno, typedef) + return SYMBOL_TABLE.declare_param(id_, lineno, typedef, is_array) def make_type(typename, lineno, implicit=False): @@ -1047,34 +1050,6 @@ def p_assignment(p): syntax_error(p.lineno(i), 'Cannot assign an array to an scalar variable') return - if variable.class_ == CLASS.array: - if q1class_ != variable.class_: - syntax_error(p.lineno(i), 'Cannot assign an scalar to an array variable') - return - - if q[1].type_ != variable.type_: - syntax_error(p.lineno(i), 'Arrays must have the same element type') - return - - if variable.memsize != q[1].memsize: - syntax_error(p.lineno(i), "Arrays '%s' and '%s' must have the same size" % - (variable.name, q[1].name)) - return - - if variable.count != q[1].count: - warning(p.lineno(i), "Arrays '%s' and '%s' don't have the same number of dimensions" % - (variable.name, q[1].name)) - else: - for b1, b2 in zip(variable.bounds, q[1].bounds): - if b1.count != b2.count: - warning(p.lineno(i), "Arrays '%s' and '%s' don't have the same dimensions" % - (variable.name, q[1].name)) - break - # Array copy - variable.accessed = True - p[0] = make_sentence('ARRAYCOPY', variable, q[1]) - return - expr = make_typecast(variable.type_, q[1], p.lineno(i)) p[0] = make_sentence('LET', variable, expr) @@ -1082,8 +1057,6 @@ def p_assignment(p): def p_lexpr(p): """ lexpr : ID EQ | LET ID EQ - | ARRAY_ID EQ - | LET ARRAY_ID EQ """ global LET_ASSIGNMENT @@ -1099,6 +1072,49 @@ def p_lexpr(p): SYMBOL_TABLE.access_id(p[i], p.lineno(i)) +def p_array_copy(p): + """ statement : ARRAY_ID EQ ARRAY_ID + | LET ARRAY_ID EQ ARRAY_ID + """ + if p[1] == 'LET': + array_id1, array_id2 = p[2], p[4] + l1, l2 = p.lineno(2), p.lineno(4) + else: + array_id1, array_id2 = p[1], p[3] + l1, l2 = p.lineno(1), p.lineno(3) + + larray = SYMBOL_TABLE.access_id(array_id1, l1) + rarray = SYMBOL_TABLE.access_id(array_id2, l2) + + if larray is None or rarray is None: + p[0] = None + return + + if larray.type_ != rarray.type_: + syntax_error(l1, 'Arrays must have the same element type') + return + + if larray.memsize != rarray.memsize: + syntax_error(l1, "Arrays '%s' and '%s' must have the same size" % + (array_id1, array_id2)) + return + + if larray.count != rarray.count: + warning(l1, "Arrays '%s' and '%s' don't have the same number of dimensions" % + (larray.name, rarray.name)) + else: + for b1, b2 in zip(larray.bounds, rarray.bounds): + if b1.count != b2.count: + warning(l1, "Arrays '%s' and '%s' don't have the same dimensions" % + (array_id1, array_id2)) + break + # Array copy + larray.accessed = True + rarray.accessed = True + p[0] = make_sentence('ARRAYCOPY', larray, rarray) + return + + def p_arr_assignment(p): """ statement : ARRAY_ID arg_list EQ expr | LET ARRAY_ID arg_list EQ expr @@ -2543,7 +2559,6 @@ def p_exprstr_file(p): def p_id_expr(p): """ bexpr : ID - | ARRAY_ID """ entry = SYMBOL_TABLE.access_id(p[1], p.lineno(1), default_class=CLASS.var) if entry is None: @@ -2557,7 +2572,7 @@ def p_id_expr(p): p[0] = entry - if entry.class_ == CLASS.array: + if entry.class_ == CLASS.array: # HINT: This should never happen now if not LET_ASSIGNMENT: syntax_error(p.lineno(1), "Variable '%s' is an array and cannot be used in this context" % p[1]) p[0] = None @@ -2769,18 +2784,40 @@ def p_arg_list_arg(p): def p_arguments(p): - """ arguments : arguments COMMA expr + """ arguments : argument + """ + if p[1] is None: + p[0] = None + return + + p[0] = make_arg_list(p[1]) + + +def p_arguments_argument(p): + """ arguments : arguments COMMA argument """ if p[1] is None or p[3] is None: p[0] = None else: - p[0] = make_arg_list(p[1], make_argument(p[3], p.lineno(2))) + p[0] = make_arg_list(p[1], p[3]) def p_argument(p): - """ arguments : expr %prec ID + """ argument : expr """ - p[0] = make_arg_list(make_argument(p[1], p.lineno(1))) + p[0] = make_argument(p[1], p.lineno(1)) + + +def p_argument_array(p): + """ argument : ARRAY_ID + """ + entry = SYMBOL_TABLE.access_array(p[1], p.lineno(1)) + if entry is None: + p[0] = None + return + + entry.accessed = True + p[0] = make_argument(entry, p.lineno(1)) def p_funcdecl(p): @@ -2960,18 +2997,42 @@ def p_param_byref_definition(p): def p_param_byval_definition(p): """ param_definition : BYVAL param_def """ - p[0] = p[2] + param_def = p[2] + p[0] = param_def if p[0] is not None: - p[0].byref = False + if param_def.class_ == CLASS.array: + api.errmsg.syntax_error_cannot_pass_array_by_value(p.lineno(1), param_def.name) + p[0] = None + return + param_def.byref = False def p_param_definition(p): """ param_definition : param_def """ - p[0] = p[1] + param_def = p[1] + p[0] = param_def if p[0] is not None: - p[0].byref = OPTIONS.byref.value + if param_def.class_ == CLASS.array: + param_def.byref = True + else: + param_def.byref = OPTIONS.byref.value + + +def p_param_def_array(p): + """ param_def : ID LP RP typedef + """ + typeref = p[4] + if typeref is None: + p[0] = None + return + + lineno = p.lineno(1) + id_ = p[1] + + api.check.check_type_is_explicit(lineno, id_, typeref) + p[0] = make_param_decl(id_, lineno, typeref, is_array=True) def p_param_def_type(p): From ff3be8e149655e79fc08abd6c3146ab6aff190ec Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Fri, 10 Apr 2020 22:25:58 +0200 Subject: [PATCH 09/14] Check typecast in array parameters Checks a function with array parameters is being called correctly. Also test updated. --- symbols/typecast.py | 6 ++++++ tests/functional/pararray2.bas | 9 +++++++++ tests/functional/test_errmsg.txt | 8 +++++--- 3 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 tests/functional/pararray2.bas diff --git a/symbols/typecast.py b/symbols/typecast.py index 81f388812..0ae1bd080 100644 --- a/symbols/typecast.py +++ b/symbols/typecast.py @@ -13,6 +13,7 @@ from .type_ import SymbolTYPE from .type_ import Type as TYPE from .number import SymbolNUMBER +from .vararray import SymbolVARARRAY from api.errmsg import syntax_error from api import errmsg @@ -60,6 +61,11 @@ def make_node(cls, new_type, node, lineno): if new_type == node.type_: return node # Do nothing. Return as is + # TODO: Create a base scalar type + if isinstance(node, SymbolVARARRAY): + syntax_error(lineno, "Array {} type does not match parameter type".format(node.name)) + return None + STRTYPE = TYPE.string # Typecasting, at the moment, only for number if node.type_ == STRTYPE: diff --git a/tests/functional/pararray2.bas b/tests/functional/pararray2.bas new file mode 100644 index 000000000..6fcabcef2 --- /dev/null +++ b/tests/functional/pararray2.bas @@ -0,0 +1,9 @@ + +FUNCTION test(q() as UInteger) as Byte + DIM i as Ubyte = 3 + q(i) = 7 +END FUNCTION + +DIM q(10) as UByte +test(q) + diff --git a/tests/functional/test_errmsg.txt b/tests/functional/test_errmsg.txt index c65bf8c3e..2c9b89b94 100644 --- a/tests/functional/test_errmsg.txt +++ b/tests/functional/test_errmsg.txt @@ -41,9 +41,9 @@ mcleod3.bas:3: 'GenerateSpaces' is neither an array nor a function. mcleod3.bas:1: warning: Parameter 'path' is never used mcleod3.bas:6: warning: Parameter 'n' is never used >>> process_file('poke3.bas') -poke3.bas:4: Variable 'a' is an array and cannot be used in this context +poke3.bas:4: Syntax Error. Unexpected token ',' >>> process_file('poke5.bas') -poke5.bas:4: Variable 'a' is an array and cannot be used in this context +poke5.bas:4: Syntax Error. Unexpected token ',' >>> process_file('arrlabels10.bas') arrlabels10.bas:3: warning: Using default implicit type 'float' for 'a' arrlabels10.bas:3: Can't convert non-numeric value to float at compile time @@ -84,7 +84,7 @@ errletfunc.bas:5: Cannot assign a value to 'x'. It's not a variable >>> process_file('read0.bas') read0.bas:12: 'x' is SUBROUTINE not a FUNCTION >>> process_file('read1.bas') -read1.bas:11: Variable 'x' is an array and cannot be used in this context +read1.bas:11: Cannot read 'x'. It's an array >>> process_file('read3.bas') read3.bas:9: 'x' is neither an array nor a function. >>> process_file('read6.bas') @@ -141,4 +141,6 @@ params_implicit.bas:2: warning: Parameter 'y' is never used array_err.bas:2: Mismatched vector size. Expected 11 elements, got 1. >>> process_file('arrbase1.bas') >>> process_file('param_byref_warn.bas') +>>> process_file('pararray2.bas') +pararray2.bas:8: Array q type does not match parameter type From 93358bbf11c41a30358d1524f66db484b009de90 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Fri, 10 Apr 2020 23:24:33 +0200 Subject: [PATCH 10/14] Return default class 'unknown' for Arguments --- symbols/argument.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/symbols/argument.py b/symbols/argument.py index f375c03c3..1d156f7a4 100644 --- a/symbols/argument.py +++ b/symbols/argument.py @@ -15,6 +15,7 @@ from .var import SymbolVAR from api.config import OPTIONS from api.constants import SCOPE +from api.constants import CLASS class SymbolARGUMENT(Symbol): @@ -56,7 +57,7 @@ def type_(self): @property def class_(self): - return self.value.class_ + return getattr(self.value, 'class_', CLASS.unknown) @property def byref(self): From 0011f4438742f630b2bc1c4dbb47aa7338d2c7fe Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Fri, 10 Apr 2020 23:24:35 +0200 Subject: [PATCH 11/14] Ensure Array and Var parameters are right When invoking a function check also their arguments (var, arrays) matches correctly. --- api/check.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/api/check.py b/api/check.py index 4329d422c..afcf692cc 100644 --- a/api/check.py +++ b/api/check.py @@ -105,6 +105,10 @@ def check_call_arguments(lineno, id_, args): return False for arg, param in zip(args, entry.params): + if arg.class_ in (CLASS.var, CLASS.array) and param.class_ != arg.class_: + syntax_error(lineno, "Invalid argument '{}'".format(arg.value)) + return None + if not arg.typecast(param.type_): return False From e2bde4ff5c23a0e0d0f17bb901e9066012238bfc Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 12 Apr 2020 12:29:18 +0200 Subject: [PATCH 12/14] Allow passing arrays of some types if size matches Only for numerical types --- symbols/typecast.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/symbols/typecast.py b/symbols/typecast.py index 0ae1bd080..8a188cfdc 100644 --- a/symbols/typecast.py +++ b/symbols/typecast.py @@ -63,6 +63,9 @@ def make_node(cls, new_type, node, lineno): # TODO: Create a base scalar type if isinstance(node, SymbolVARARRAY): + if new_type.size == node.type_.size and TYPE.string not in (new_type, node.type_): + return node + syntax_error(lineno, "Array {} type does not match parameter type".format(node.name)) return None From 81f91ff461584e6b90bd45cc7fe4e8c4f07a28e9 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Fri, 10 Apr 2020 13:38:31 +0200 Subject: [PATCH 13/14] Test added for array param syntax --- tests/functional/pararray0.asm | 219 +++++++++++++++++++++++ tests/functional/pararray0.bas | 8 + tests/functional/pararray1.asm | 234 +++++++++++++++++++++++++ tests/functional/pararray1.bas | 9 + tests/functional/pararray3.bas | 9 + tests/functional/pararray4.bas | 9 + tests/functional/pararray5.asm | 234 +++++++++++++++++++++++++ tests/functional/pararray5.bas | 9 + tests/functional/pararray6.asm | 237 +++++++++++++++++++++++++ tests/functional/pararray6.bas | 9 + tests/functional/pararray7.asm | 288 +++++++++++++++++++++++++++++++ tests/functional/pararray7.bas | 11 ++ tests/functional/test_errmsg.txt | 2 + 13 files changed, 1278 insertions(+) create mode 100644 tests/functional/pararray0.asm create mode 100644 tests/functional/pararray0.bas create mode 100644 tests/functional/pararray1.asm create mode 100644 tests/functional/pararray1.bas create mode 100644 tests/functional/pararray3.bas create mode 100644 tests/functional/pararray4.bas create mode 100644 tests/functional/pararray5.asm create mode 100644 tests/functional/pararray5.bas create mode 100644 tests/functional/pararray6.asm create mode 100644 tests/functional/pararray6.bas create mode 100644 tests/functional/pararray7.asm create mode 100644 tests/functional/pararray7.bas diff --git a/tests/functional/pararray0.asm b/tests/functional/pararray0.asm new file mode 100644 index 000000000..0acd37f89 --- /dev/null +++ b/tests/functional/pararray0.asm @@ -0,0 +1,219 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, _q + push hl + call _test + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +_test: + push ix + ld ix, 0 + add ix, sp + ld hl, 3 + push hl + push ix + pop hl + ld de, 4 + add hl, de + call __ARRAY_PTR + ld de, 7 + ld (hl), e + inc hl + ld (hl), d +_test__leave: + ld sp, ix + pop ix + exx + pop hl + ex (sp), hl + exx + ret +#line 1 "array.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; ------------------------------------------------------------------- + ; Simple array Index routine + ; Number of total indexes dimensions - 1 at beginning of memory + ; HL = Start of array memory (First two bytes contains N-1 dimensions) + ; Dimension values on the stack, (top of the stack, highest dimension) + ; E.g. A(2, 4) -> PUSH <4>; PUSH <2> + ; For any array of N dimension A(aN-1, ..., a1, a0) + ; and dimensions D[bN-1, ..., b1, b0], the offset is calculated as + ; O = [a0 + b0 * (a1 + b1 * (a2 + ... bN-2(aN-1)))] +; What I will do here is to calculate the following sequence: + ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... +#line 1 "mul16.asm" +__MUL16: ; Mutiplies HL with the last value stored into de stack + ; Works for both signed and unsigned + PROC + LOCAL __MUL16LOOP + LOCAL __MUL16NOADD + ex de, hl + pop hl ; Return address + ex (sp), hl ; CALLEE caller convention +__MUL16_FAST: + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +__MUL16LOOP: + add hl, hl ; hl << 1 + sla c + rla ; a,c << 1 + jp nc, __MUL16NOADD + add hl, de +__MUL16NOADD: + djnz __MUL16LOOP + ret ; Result in hl (16 lower bits) + ENDP +#line 20 "array.asm" +#line 24 "/zxbasic/library-asm/array.asm" +__ARRAY_PTR: ;; computes an array offset from a pointer + ld c, (hl) + inc hl + ld h, (hl) + ld l, c +__ARRAY: + PROC + LOCAL LOOP + LOCAL ARRAY_END + LOCAL RET_ADDRESS ; Stores return address + LOCAL TMP_ARR_PTR ; Stores pointer temporarily + ld e, (hl) + inc hl + ld d, (hl) + inc hl + ld (TMP_ARR_PTR), hl + ex de, hl + ex (sp), hl ; Return address in HL, array address in the stack + ld (RET_ADDRESS + 1), hl ; Stores it for later + exx + pop hl ; Will use H'L' as the pointer + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + ld hl, 0 ; HL = Offset "accumulator" +LOOP: +#line 62 "/zxbasic/library-asm/array.asm" + pop bc ; Get next index (Ai) from the stack +#line 72 "/zxbasic/library-asm/array.asm" + add hl, bc ; Adds current index + exx ; Checks if B'C' = 0 + ld a, b ; Which means we must exit (last element is not multiplied by anything) + or c + jr z, ARRAY_END ; if B'Ci == 0 we are done + ld e, (hl) ; Loads next dimension into D'E' + inc hl + ld d, (hl) + inc hl + push de + dec bc ; Decrements loop counter + exx + pop de ; DE = Max bound Number (i-th dimension) + call __FNMUL + jp LOOP +ARRAY_END: + ld a, (hl) + exx +#line 101 "/zxbasic/library-asm/array.asm" + LOCAL ARRAY_SIZE_LOOP + ex de, hl + ld hl, 0 + ld b, a +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP +#line 111 "/zxbasic/library-asm/array.asm" + ex de, hl + ld hl, (TMP_ARR_PTR) + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + add hl, de ; Adds element start +RET_ADDRESS: + jp 0 + ;; Performs a faster multiply for little 16bit numbs + LOCAL __FNMUL, __FNMUL2 +__FNMUL: + xor a + or h + jp nz, __MUL16_FAST + or l + ret z + cp 33 + jp nc, __MUL16_FAST + ld b, l + ld l, h ; HL = 0 +__FNMUL2: + add hl, de + djnz __FNMUL2 + ret +TMP_ARR_PTR: + DW 0 ; temporary storage for pointer to tables + ENDP +#line 44 "pararray0.bas" +ZXBASIC_USER_DATA: +_q: + DEFW __LABEL0 +_q.__DATA__.__PTR__: + DEFW _q.__DATA__ +_q.__DATA__: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +__LABEL0: + DEFW 0000h + DEFB 02h +; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END: + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/pararray0.bas b/tests/functional/pararray0.bas new file mode 100644 index 000000000..33c134ed3 --- /dev/null +++ b/tests/functional/pararray0.bas @@ -0,0 +1,8 @@ + +FUNCTION test(q() as UInteger) as Byte + q(3) = 7 +END FUNCTION + +DIM q(10) as UInteger +test(q) + diff --git a/tests/functional/pararray1.asm b/tests/functional/pararray1.asm new file mode 100644 index 000000000..5f2d8f960 --- /dev/null +++ b/tests/functional/pararray1.asm @@ -0,0 +1,234 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, _q + push hl + call _test + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +_test: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + inc sp + push ix + pop hl + ld bc, -1 + add hl, bc + ex de, hl + ld hl, __LABEL0 + ld bc, 1 + ldir + ld a, (ix-1) + ld l, a + ld h, 0 + push hl + push ix + pop hl + ld de, 4 + add hl, de + call __ARRAY_PTR + ld de, 7 + ld (hl), e + inc hl + ld (hl), d +_test__leave: + ld sp, ix + pop ix + exx + pop hl + ex (sp), hl + exx + ret +#line 1 "array.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; ------------------------------------------------------------------- + ; Simple array Index routine + ; Number of total indexes dimensions - 1 at beginning of memory + ; HL = Start of array memory (First two bytes contains N-1 dimensions) + ; Dimension values on the stack, (top of the stack, highest dimension) + ; E.g. A(2, 4) -> PUSH <4>; PUSH <2> + ; For any array of N dimension A(aN-1, ..., a1, a0) + ; and dimensions D[bN-1, ..., b1, b0], the offset is calculated as + ; O = [a0 + b0 * (a1 + b1 * (a2 + ... bN-2(aN-1)))] +; What I will do here is to calculate the following sequence: + ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... +#line 1 "mul16.asm" +__MUL16: ; Mutiplies HL with the last value stored into de stack + ; Works for both signed and unsigned + PROC + LOCAL __MUL16LOOP + LOCAL __MUL16NOADD + ex de, hl + pop hl ; Return address + ex (sp), hl ; CALLEE caller convention +__MUL16_FAST: + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +__MUL16LOOP: + add hl, hl ; hl << 1 + sla c + rla ; a,c << 1 + jp nc, __MUL16NOADD + add hl, de +__MUL16NOADD: + djnz __MUL16LOOP + ret ; Result in hl (16 lower bits) + ENDP +#line 20 "array.asm" +#line 24 "/zxbasic/library-asm/array.asm" +__ARRAY_PTR: ;; computes an array offset from a pointer + ld c, (hl) + inc hl + ld h, (hl) + ld l, c +__ARRAY: + PROC + LOCAL LOOP + LOCAL ARRAY_END + LOCAL RET_ADDRESS ; Stores return address + LOCAL TMP_ARR_PTR ; Stores pointer temporarily + ld e, (hl) + inc hl + ld d, (hl) + inc hl + ld (TMP_ARR_PTR), hl + ex de, hl + ex (sp), hl ; Return address in HL, array address in the stack + ld (RET_ADDRESS + 1), hl ; Stores it for later + exx + pop hl ; Will use H'L' as the pointer + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + ld hl, 0 ; HL = Offset "accumulator" +LOOP: +#line 62 "/zxbasic/library-asm/array.asm" + pop bc ; Get next index (Ai) from the stack +#line 72 "/zxbasic/library-asm/array.asm" + add hl, bc ; Adds current index + exx ; Checks if B'C' = 0 + ld a, b ; Which means we must exit (last element is not multiplied by anything) + or c + jr z, ARRAY_END ; if B'Ci == 0 we are done + ld e, (hl) ; Loads next dimension into D'E' + inc hl + ld d, (hl) + inc hl + push de + dec bc ; Decrements loop counter + exx + pop de ; DE = Max bound Number (i-th dimension) + call __FNMUL + jp LOOP +ARRAY_END: + ld a, (hl) + exx +#line 101 "/zxbasic/library-asm/array.asm" + LOCAL ARRAY_SIZE_LOOP + ex de, hl + ld hl, 0 + ld b, a +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP +#line 111 "/zxbasic/library-asm/array.asm" + ex de, hl + ld hl, (TMP_ARR_PTR) + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + add hl, de ; Adds element start +RET_ADDRESS: + jp 0 + ;; Performs a faster multiply for little 16bit numbs + LOCAL __FNMUL, __FNMUL2 +__FNMUL: + xor a + or h + jp nz, __MUL16_FAST + or l + ret z + cp 33 + jp nc, __MUL16_FAST + ld b, l + ld l, h ; HL = 0 +__FNMUL2: + add hl, de + djnz __FNMUL2 + ret +TMP_ARR_PTR: + DW 0 ; temporary storage for pointer to tables + ENDP +#line 57 "pararray1.bas" +ZXBASIC_USER_DATA: +_q: + DEFW __LABEL1 +_q.__DATA__.__PTR__: + DEFW _q.__DATA__ +_q.__DATA__: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +__LABEL1: + DEFW 0000h + DEFB 02h +__LABEL0: + DEFB 03h +; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END: + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/pararray1.bas b/tests/functional/pararray1.bas new file mode 100644 index 000000000..62cdedfd5 --- /dev/null +++ b/tests/functional/pararray1.bas @@ -0,0 +1,9 @@ + +FUNCTION test(q() as UInteger) as Byte + DIM i as Ubyte = 3 + q(i) = 7 +END FUNCTION + +DIM q(10) as UInteger +test(q) + diff --git a/tests/functional/pararray3.bas b/tests/functional/pararray3.bas new file mode 100644 index 000000000..78c2b7326 --- /dev/null +++ b/tests/functional/pararray3.bas @@ -0,0 +1,9 @@ + +FUNCTION test(q as UInteger) as Byte + DIM i as Ubyte = 3 + q = 7 +END FUNCTION + +DIM q(10) as UInteger +test(q) + diff --git a/tests/functional/pararray4.bas b/tests/functional/pararray4.bas new file mode 100644 index 000000000..1b1007adf --- /dev/null +++ b/tests/functional/pararray4.bas @@ -0,0 +1,9 @@ + +FUNCTION test(q() as UInteger) as Byte + DIM i as Ubyte = 3 + q(i) = 7 +END FUNCTION + +DIM q(10) as String +test(q) + diff --git a/tests/functional/pararray5.asm b/tests/functional/pararray5.asm new file mode 100644 index 000000000..f02c009a8 --- /dev/null +++ b/tests/functional/pararray5.asm @@ -0,0 +1,234 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, _q + push hl + call _test + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +_test: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + inc sp + push ix + pop hl + ld bc, -1 + add hl, bc + ex de, hl + ld hl, __LABEL0 + ld bc, 1 + ldir + ld a, (ix-1) + ld l, a + ld h, 0 + push hl + push ix + pop hl + ld de, 4 + add hl, de + call __ARRAY_PTR + ld de, 7 + ld (hl), e + inc hl + ld (hl), d +_test__leave: + ld sp, ix + pop ix + exx + pop hl + ex (sp), hl + exx + ret +#line 1 "array.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; ------------------------------------------------------------------- + ; Simple array Index routine + ; Number of total indexes dimensions - 1 at beginning of memory + ; HL = Start of array memory (First two bytes contains N-1 dimensions) + ; Dimension values on the stack, (top of the stack, highest dimension) + ; E.g. A(2, 4) -> PUSH <4>; PUSH <2> + ; For any array of N dimension A(aN-1, ..., a1, a0) + ; and dimensions D[bN-1, ..., b1, b0], the offset is calculated as + ; O = [a0 + b0 * (a1 + b1 * (a2 + ... bN-2(aN-1)))] +; What I will do here is to calculate the following sequence: + ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... +#line 1 "mul16.asm" +__MUL16: ; Mutiplies HL with the last value stored into de stack + ; Works for both signed and unsigned + PROC + LOCAL __MUL16LOOP + LOCAL __MUL16NOADD + ex de, hl + pop hl ; Return address + ex (sp), hl ; CALLEE caller convention +__MUL16_FAST: + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +__MUL16LOOP: + add hl, hl ; hl << 1 + sla c + rla ; a,c << 1 + jp nc, __MUL16NOADD + add hl, de +__MUL16NOADD: + djnz __MUL16LOOP + ret ; Result in hl (16 lower bits) + ENDP +#line 20 "array.asm" +#line 24 "/zxbasic/library-asm/array.asm" +__ARRAY_PTR: ;; computes an array offset from a pointer + ld c, (hl) + inc hl + ld h, (hl) + ld l, c +__ARRAY: + PROC + LOCAL LOOP + LOCAL ARRAY_END + LOCAL RET_ADDRESS ; Stores return address + LOCAL TMP_ARR_PTR ; Stores pointer temporarily + ld e, (hl) + inc hl + ld d, (hl) + inc hl + ld (TMP_ARR_PTR), hl + ex de, hl + ex (sp), hl ; Return address in HL, array address in the stack + ld (RET_ADDRESS + 1), hl ; Stores it for later + exx + pop hl ; Will use H'L' as the pointer + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + ld hl, 0 ; HL = Offset "accumulator" +LOOP: +#line 62 "/zxbasic/library-asm/array.asm" + pop bc ; Get next index (Ai) from the stack +#line 72 "/zxbasic/library-asm/array.asm" + add hl, bc ; Adds current index + exx ; Checks if B'C' = 0 + ld a, b ; Which means we must exit (last element is not multiplied by anything) + or c + jr z, ARRAY_END ; if B'Ci == 0 we are done + ld e, (hl) ; Loads next dimension into D'E' + inc hl + ld d, (hl) + inc hl + push de + dec bc ; Decrements loop counter + exx + pop de ; DE = Max bound Number (i-th dimension) + call __FNMUL + jp LOOP +ARRAY_END: + ld a, (hl) + exx +#line 101 "/zxbasic/library-asm/array.asm" + LOCAL ARRAY_SIZE_LOOP + ex de, hl + ld hl, 0 + ld b, a +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP +#line 111 "/zxbasic/library-asm/array.asm" + ex de, hl + ld hl, (TMP_ARR_PTR) + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + add hl, de ; Adds element start +RET_ADDRESS: + jp 0 + ;; Performs a faster multiply for little 16bit numbs + LOCAL __FNMUL, __FNMUL2 +__FNMUL: + xor a + or h + jp nz, __MUL16_FAST + or l + ret z + cp 33 + jp nc, __MUL16_FAST + ld b, l + ld l, h ; HL = 0 +__FNMUL2: + add hl, de + djnz __FNMUL2 + ret +TMP_ARR_PTR: + DW 0 ; temporary storage for pointer to tables + ENDP +#line 57 "pararray5.bas" +ZXBASIC_USER_DATA: +_q: + DEFW __LABEL1 +_q.__DATA__.__PTR__: + DEFW _q.__DATA__ +_q.__DATA__: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +__LABEL1: + DEFW 0000h + DEFB 02h +__LABEL0: + DEFB 03h +; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END: + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/pararray5.bas b/tests/functional/pararray5.bas new file mode 100644 index 000000000..8db61d66b --- /dev/null +++ b/tests/functional/pararray5.bas @@ -0,0 +1,9 @@ + +FUNCTION test(q() as UInteger) as Byte + DIM i as Ubyte = 3 + q(i) = 7 +END FUNCTION + +DIM q(10) as Integer +test(q) + diff --git a/tests/functional/pararray6.asm b/tests/functional/pararray6.asm new file mode 100644 index 000000000..dbf85896f --- /dev/null +++ b/tests/functional/pararray6.asm @@ -0,0 +1,237 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, _q + push hl + ld a, 2 + push af + call _test + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +_test: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + inc sp + push ix + pop hl + ld bc, -1 + add hl, bc + ex de, hl + ld hl, __LABEL0 + ld bc, 1 + ldir + ld a, (ix-1) + ld l, a + ld h, 0 + push hl + push ix + pop hl + ld de, 6 + add hl, de + call __ARRAY_PTR + ld de, 7 + ld (hl), e + inc hl + ld (hl), d +_test__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + ex (sp), hl + exx + ret +#line 1 "array.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; ------------------------------------------------------------------- + ; Simple array Index routine + ; Number of total indexes dimensions - 1 at beginning of memory + ; HL = Start of array memory (First two bytes contains N-1 dimensions) + ; Dimension values on the stack, (top of the stack, highest dimension) + ; E.g. A(2, 4) -> PUSH <4>; PUSH <2> + ; For any array of N dimension A(aN-1, ..., a1, a0) + ; and dimensions D[bN-1, ..., b1, b0], the offset is calculated as + ; O = [a0 + b0 * (a1 + b1 * (a2 + ... bN-2(aN-1)))] +; What I will do here is to calculate the following sequence: + ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... +#line 1 "mul16.asm" +__MUL16: ; Mutiplies HL with the last value stored into de stack + ; Works for both signed and unsigned + PROC + LOCAL __MUL16LOOP + LOCAL __MUL16NOADD + ex de, hl + pop hl ; Return address + ex (sp), hl ; CALLEE caller convention +__MUL16_FAST: + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +__MUL16LOOP: + add hl, hl ; hl << 1 + sla c + rla ; a,c << 1 + jp nc, __MUL16NOADD + add hl, de +__MUL16NOADD: + djnz __MUL16LOOP + ret ; Result in hl (16 lower bits) + ENDP +#line 20 "array.asm" +#line 24 "/zxbasic/library-asm/array.asm" +__ARRAY_PTR: ;; computes an array offset from a pointer + ld c, (hl) + inc hl + ld h, (hl) + ld l, c +__ARRAY: + PROC + LOCAL LOOP + LOCAL ARRAY_END + LOCAL RET_ADDRESS ; Stores return address + LOCAL TMP_ARR_PTR ; Stores pointer temporarily + ld e, (hl) + inc hl + ld d, (hl) + inc hl + ld (TMP_ARR_PTR), hl + ex de, hl + ex (sp), hl ; Return address in HL, array address in the stack + ld (RET_ADDRESS + 1), hl ; Stores it for later + exx + pop hl ; Will use H'L' as the pointer + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + ld hl, 0 ; HL = Offset "accumulator" +LOOP: +#line 62 "/zxbasic/library-asm/array.asm" + pop bc ; Get next index (Ai) from the stack +#line 72 "/zxbasic/library-asm/array.asm" + add hl, bc ; Adds current index + exx ; Checks if B'C' = 0 + ld a, b ; Which means we must exit (last element is not multiplied by anything) + or c + jr z, ARRAY_END ; if B'Ci == 0 we are done + ld e, (hl) ; Loads next dimension into D'E' + inc hl + ld d, (hl) + inc hl + push de + dec bc ; Decrements loop counter + exx + pop de ; DE = Max bound Number (i-th dimension) + call __FNMUL + jp LOOP +ARRAY_END: + ld a, (hl) + exx +#line 101 "/zxbasic/library-asm/array.asm" + LOCAL ARRAY_SIZE_LOOP + ex de, hl + ld hl, 0 + ld b, a +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP +#line 111 "/zxbasic/library-asm/array.asm" + ex de, hl + ld hl, (TMP_ARR_PTR) + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + add hl, de ; Adds element start +RET_ADDRESS: + jp 0 + ;; Performs a faster multiply for little 16bit numbs + LOCAL __FNMUL, __FNMUL2 +__FNMUL: + xor a + or h + jp nz, __MUL16_FAST + or l + ret z + cp 33 + jp nc, __MUL16_FAST + ld b, l + ld l, h ; HL = 0 +__FNMUL2: + add hl, de + djnz __FNMUL2 + ret +TMP_ARR_PTR: + DW 0 ; temporary storage for pointer to tables + ENDP +#line 60 "pararray6.bas" +ZXBASIC_USER_DATA: +_q: + DEFW __LABEL1 +_q.__DATA__.__PTR__: + DEFW _q.__DATA__ +_q.__DATA__: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +__LABEL1: + DEFW 0000h + DEFB 02h +__LABEL0: + DEFB 03h +; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END: + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/pararray6.bas b/tests/functional/pararray6.bas new file mode 100644 index 000000000..280d29b02 --- /dev/null +++ b/tests/functional/pararray6.bas @@ -0,0 +1,9 @@ + +FUNCTION test(a as Byte, q() as UInteger) as Byte + DIM i as Ubyte = 3 + q(i) = 7 +END FUNCTION + +DIM q(10) as Integer +test(2, q) + diff --git a/tests/functional/pararray7.asm b/tests/functional/pararray7.asm new file mode 100644 index 000000000..eec86be7f --- /dev/null +++ b/tests/functional/pararray7.asm @@ -0,0 +1,288 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, _q + push hl + ld hl, _z + push hl + call _test + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +_test: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + inc sp + push ix + pop hl + ld bc, -1 + add hl, bc + ex de, hl + ld hl, __LABEL0 + ld bc, 1 + ldir + ld a, (ix-1) + ld l, a + ld h, 0 + push hl + push ix + pop hl + ld de, 6 + add hl, de + call __ARRAY_PTR + ld de, 7 + ld (hl), e + inc hl + ld (hl), d + ld hl, 1 + push hl + ld a, (ix-1) + ld l, a + ld h, 0 + push hl + push ix + pop hl + ld de, 4 + add hl, de + call __ARRAY_PTR + ld (hl), 8 +_test__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + ex (sp), hl + exx + ret +#line 1 "array.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; ------------------------------------------------------------------- + ; Simple array Index routine + ; Number of total indexes dimensions - 1 at beginning of memory + ; HL = Start of array memory (First two bytes contains N-1 dimensions) + ; Dimension values on the stack, (top of the stack, highest dimension) + ; E.g. A(2, 4) -> PUSH <4>; PUSH <2> + ; For any array of N dimension A(aN-1, ..., a1, a0) + ; and dimensions D[bN-1, ..., b1, b0], the offset is calculated as + ; O = [a0 + b0 * (a1 + b1 * (a2 + ... bN-2(aN-1)))] +; What I will do here is to calculate the following sequence: + ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... +#line 1 "mul16.asm" +__MUL16: ; Mutiplies HL with the last value stored into de stack + ; Works for both signed and unsigned + PROC + LOCAL __MUL16LOOP + LOCAL __MUL16NOADD + ex de, hl + pop hl ; Return address + ex (sp), hl ; CALLEE caller convention +__MUL16_FAST: + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +__MUL16LOOP: + add hl, hl ; hl << 1 + sla c + rla ; a,c << 1 + jp nc, __MUL16NOADD + add hl, de +__MUL16NOADD: + djnz __MUL16LOOP + ret ; Result in hl (16 lower bits) + ENDP +#line 20 "array.asm" +#line 24 "/zxbasic/library-asm/array.asm" +__ARRAY_PTR: ;; computes an array offset from a pointer + ld c, (hl) + inc hl + ld h, (hl) + ld l, c +__ARRAY: + PROC + LOCAL LOOP + LOCAL ARRAY_END + LOCAL RET_ADDRESS ; Stores return address + LOCAL TMP_ARR_PTR ; Stores pointer temporarily + ld e, (hl) + inc hl + ld d, (hl) + inc hl + ld (TMP_ARR_PTR), hl + ex de, hl + ex (sp), hl ; Return address in HL, array address in the stack + ld (RET_ADDRESS + 1), hl ; Stores it for later + exx + pop hl ; Will use H'L' as the pointer + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + ld hl, 0 ; HL = Offset "accumulator" +LOOP: +#line 62 "/zxbasic/library-asm/array.asm" + pop bc ; Get next index (Ai) from the stack +#line 72 "/zxbasic/library-asm/array.asm" + add hl, bc ; Adds current index + exx ; Checks if B'C' = 0 + ld a, b ; Which means we must exit (last element is not multiplied by anything) + or c + jr z, ARRAY_END ; if B'Ci == 0 we are done + ld e, (hl) ; Loads next dimension into D'E' + inc hl + ld d, (hl) + inc hl + push de + dec bc ; Decrements loop counter + exx + pop de ; DE = Max bound Number (i-th dimension) + call __FNMUL + jp LOOP +ARRAY_END: + ld a, (hl) + exx +#line 101 "/zxbasic/library-asm/array.asm" + LOCAL ARRAY_SIZE_LOOP + ex de, hl + ld hl, 0 + ld b, a +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP +#line 111 "/zxbasic/library-asm/array.asm" + ex de, hl + ld hl, (TMP_ARR_PTR) + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + add hl, de ; Adds element start +RET_ADDRESS: + jp 0 + ;; Performs a faster multiply for little 16bit numbs + LOCAL __FNMUL, __FNMUL2 +__FNMUL: + xor a + or h + jp nz, __MUL16_FAST + or l + ret z + cp 33 + jp nc, __MUL16_FAST + ld b, l + ld l, h ; HL = 0 +__FNMUL2: + add hl, de + djnz __FNMUL2 + ret +TMP_ARR_PTR: + DW 0 ; temporary storage for pointer to tables + ENDP +#line 72 "pararray7.bas" +ZXBASIC_USER_DATA: +_z: + DEFW __LABEL1 +_z.__DATA__.__PTR__: + DEFW _z.__DATA__ +_z.__DATA__: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +__LABEL1: + DEFW 0001h + DEFW 0005h + DEFB 01h +_q: + DEFW __LABEL2 +_q.__DATA__.__PTR__: + DEFW _q.__DATA__ +_q.__DATA__: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +__LABEL2: + DEFW 0000h + DEFB 02h +__LABEL0: + DEFB 03h +; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END: + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/pararray7.bas b/tests/functional/pararray7.bas new file mode 100644 index 000000000..9d28f4e1c --- /dev/null +++ b/tests/functional/pararray7.bas @@ -0,0 +1,11 @@ + +FUNCTION test(a() as Byte, q() as UInteger) as Byte + DIM i as Ubyte = 3 + q(i) = 7 + a(i, 1) = 8 +END FUNCTION + +DIM z(5, 4) as UByte +DIM q(10) as Integer +test(z, q) + diff --git a/tests/functional/test_errmsg.txt b/tests/functional/test_errmsg.txt index 2c9b89b94..3932fe22b 100644 --- a/tests/functional/test_errmsg.txt +++ b/tests/functional/test_errmsg.txt @@ -143,4 +143,6 @@ array_err.bas:2: Mismatched vector size. Expected 11 elements, got 1. >>> process_file('param_byref_warn.bas') >>> process_file('pararray2.bas') pararray2.bas:8: Array q type does not match parameter type +>>> process_file('pararray4.bas') +pararray4.bas:8: Array q type does not match parameter type From 4503da12d61f4aac3500e77017b9999423214058 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Mon, 13 Apr 2020 01:41:40 +0200 Subject: [PATCH 14/14] Update tests for Symbol ArrayAccess This requires now to have uinteger type --- tests/symbols/test_symbolARRAYACCESS.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/symbols/test_symbolARRAYACCESS.py b/tests/symbols/test_symbolARRAYACCESS.py index 027cd5cca..f97987457 100644 --- a/tests/symbols/test_symbolARRAYACCESS.py +++ b/tests/symbols/test_symbolARRAYACCESS.py @@ -24,8 +24,8 @@ def setUp(self): c = symbols.BOUND(l3, l4) self.bounds = symbols.BOUNDLIST.make_node(None, b, c) self.arr = symbols.VARARRAY('test', self.bounds, 1, type_=Type.ubyte) - self.arg = symbols.ARGLIST(symbols.ARGUMENT(symbols.NUMBER(2, 1), 1), - symbols.ARGUMENT(symbols.NUMBER(3, 1), 1)) + self.arg = symbols.ARGLIST(symbols.ARGUMENT(symbols.NUMBER(2, 1, type_=Type.uinteger), 1), + symbols.ARGUMENT(symbols.NUMBER(3, 1, type_=Type.uinteger), 1)) gl.SYMBOL_TABLE = SymbolTable() # Clears stderr and prepares for capturing it config.OPTIONS.remove_option('stderr')