Skip to content
This repository has been archived by the owner on May 21, 2024. It is now read-only.

Commit

Permalink
Merge pull request boriel-basic#275 from boriel/feature/array_as_param
Browse files Browse the repository at this point in the history
Feature/array as param
  • Loading branch information
boriel committed Apr 12, 2020
2 parents 6f49ec1 + 4503da1 commit 3210892
Show file tree
Hide file tree
Showing 27 changed files with 1,471 additions and 77 deletions.
4 changes: 4 additions & 0 deletions api/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
7 changes: 7 additions & 0 deletions api/errmsg.py
Original file line number Diff line number Diff line change
Expand Up @@ -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_)
5 changes: 5 additions & 0 deletions api/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
11 changes: 9 additions & 2 deletions api/symboltable.py
Original file line number Diff line number Diff line change
Expand Up @@ -685,17 +685,24 @@ 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.
"""
if not self.check_is_undeclared(id_, lineno, classname='parameter',
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_)
Expand Down
12 changes: 7 additions & 5 deletions arch/zx48k/translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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 = []
Expand Down
3 changes: 2 additions & 1 deletion symbols/argument.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand Down
44 changes: 25 additions & 19 deletions symbols/arrayaccess.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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):
Expand Down Expand Up @@ -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
Expand All @@ -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)
4 changes: 3 additions & 1 deletion symbols/call.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion symbols/paramlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
9 changes: 9 additions & 0 deletions symbols/typecast.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -60,6 +61,14 @@ 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):
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

STRTYPE = TYPE.string
# Typecasting, at the moment, only for number
if node.type_ == STRTYPE:
Expand Down
3 changes: 2 additions & 1 deletion symbols/vararray.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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):
Expand Down

0 comments on commit 3210892

Please sign in to comment.