def access_id(self, id_, lineno, scope=None, default_type=None, default_class=CLASS.unknown): """ Access a symbol by its identifier and checks if it exists. If not, it's supposed to be an implicit declared variable. default_class is the class to use in case of an undeclared-implicit-accessed id """ if isinstance(default_type, symbols.BASICTYPE): default_type = symbols.TYPEREF(default_type, lineno, implicit=False) assert default_type is None or isinstance(default_type, symbols.TYPEREF) if not check_is_declared_explicit(lineno, id_): return None result = self.get_entry(id_, scope) if result is None: if default_type is None: default_type = symbols.TYPEREF(self.basic_types[global_.DEFAULT_IMPLICIT_TYPE], lineno, implicit=True) result = self.declare_variable(id_, lineno, default_type) result.declared = False # It was implicitly declared result.class_ = default_class return result # The entry was already declared. If it's type is auto and the default type is not None, # update its type. if default_type is not None and result.type_ == self.basic_types[TYPE.auto]: result.type_ = default_type warning_implicit_type(lineno, id_, default_type) return result
def access_func(self, id_, lineno, scope=None, default_type=None): """ Since ZX BASIC allows access to undeclared functions, we must allow and *implicitly* declare them if they are not declared already. This function just checks if the id_ exists and returns its entry if so. Otherwise, creates an implicit declared variable entry and returns it. """ assert default_type is None or isinstance(default_type, symbols.TYPEREF) result = self.get_entry(id_, scope) if result is None: if default_type is None: if global_.DEFAULT_IMPLICIT_TYPE == TYPE.auto: default_type = symbols.TYPEREF(self.basic_types[TYPE.auto], lineno, implicit=True) else: default_type = symbols.TYPEREF( self.basic_types[global_.DEFAULT_TYPE], lineno, implicit=True) return self.declare_func(id_, lineno, default_type) if not self.check_class(id_, CLASS.function, lineno, scope): return None return result
def test_offset(self): gl.SYMBOL_TABLE.declare_array('test', 1, symbols.TYPEREF(self.arr.type_, 1), bounds=self.bounds) aa = symbols.ARRAYACCESS.make_node('test', self.arg, lineno=2) self.assertIsInstance(aa, symbols.ARRAYACCESS) self.assertIsNotNone(aa.offset) self.assertEqual(aa.offset, 2)
def test_make_node_warn(self): gl.SYMBOL_TABLE.declare_array('test', 1, symbols.TYPEREF(self.arr.type_, 1), bounds=self.bounds) self.arg[1] = symbols.ARGUMENT(symbols.NUMBER(9, 1), 1) aa = symbols.ARRAYACCESS.make_node('test', self.arg, lineno=2) self.assertIsNotNone(aa) self.assertEqual(self.OUTPUT, "(stdin):2: warning: Array 'test' subscript out of range\n")
def test_make_node_fail(self): gl.SYMBOL_TABLE.declare_array('test', 1, symbols.TYPEREF(self.arr.type_, 1), bounds=self.bounds) self.arg = symbols.ARGLIST(symbols.ARGUMENT(symbols.NUMBER(2, 1), 1)) aa = symbols.ARRAYACCESS.make_node('test', self.arg, lineno=2) self.assertIsNone(aa) self.assertEqual(self.OUTPUT, "(stdin):2: Array 'test' has 2 dimensions, not 1\n")
def declare(self, id_: str, lineno: int, entry): """ Check there is no 'id' already declared in the current scope, and creates and returns it. Otherwise, returns None, and the caller function raises the syntax/semantic error. Parameter entry is the SymbolVAR, SymbolVARARRAY, etc. instance The entry 'declared' field is leave untouched. Setting it if on behalf of the caller. """ id2 = id_ type_ = entry.type_ if id2[-1] in DEPRECATED_SUFFIXES: id2 = id2[:-1] # Remove it type_ = symbols.TYPEREF(self.basic_types[SUFFIX_TYPE[id_[-1]]], lineno) # Overrides type_ if entry.type_ is not None and not entry.type_.implicit and type_ != entry.type_: syntax_error( lineno, "expected type {2} for '{0}', got {1}".format( id_, entry.type_.name, type_.name)) # Checks if already declared if self[self.current_scope][id2] is not None: return None entry.caseins = OPTIONS.case_insensitive.value self[self.current_scope][id2] = entry entry.name = id2 # Removes DEPRECATED SUFFIXES if any if isinstance(entry, symbols.TYPE): return entry # If it's a type declaration, we're done # HINT: The following should be done by the respective callers! # entry.callable = None # True if function, strings or arrays # entry.class_ = None # TODO: important entry.forwarded = False # True for a function header entry.mangled = '%s%s%s' % (self.mangle, global_.MANGLE_CHR, entry.name ) # Mangled name entry.type_ = type_ # type_ now reflects entry sigil (i.e. a$ => 'string' type) if any entry.scopeRef = self[self.current_scope] return entry
def btyperef(self, type_): assert TYPE.is_valid(type_) return symbols.TYPEREF(symbols.BASICTYPE(type_), 0)