def load_package(include_stmt, extern=False): # package name name = '' # if it is anonymous used = False # alias if necessary alias = None for item in include_stmt.content: if isinstance(item, ASTNode): # update name to be end of suffix if item.name == 'dot_id': name = unparse(item)[-1].value # if this sub tree exists, that means the inclusion is used elif item.name == 'use': used = True # if there is a rename, that means an alias was used elif item.name == 'rename': alias = item.content[0].value[1:-1] if not re.match(r'[^\d\W]\w*', alias): errormodule.throw('package_error', 'Invalid package name', include_stmt) else: # get base name if item.type == 'IDENTIFIER': name = item.value # package cannot be used and external if used and extern: errormodule.throw('package_error', 'Package cannot be both external and anonymous.', include_stmt) # if get fails due to file path not being found try: code, path = get(name) except FileNotFoundError: errormodule.throw('package_error', 'Unable to locate package by name \'%s\'.' % name, include_stmt) return # prevent redundant imports if name in imports: ast = imports[name] # if working directory needs to be updated elif path: # store cwd cwd = os.getcwd() # chdir to the parent directory of the file to allow for local importing os.chdir(path) # get the data necessary ast = get_ast(code) # change back to cwd os.chdir(cwd) else: # just get the ast, no cwd recursion necessary ast = get_ast(code) # set alias if there was none provided if not alias: alias = name if name not in imports: imports[name] = ast return util.Package(alias, extern, used, ast)
def warn(message, params): if isinstance(params, ASTNode): raw_tokens = unparse(params) else: raw_tokens = [params] line, ndx = get_position(raw_tokens[0].ndx) print('[WARNING] %s %s' % (message, '[line:%d position:%d]' % (line, ndx)))
def _print_semantic_error(message, params): if isinstance(params, ASTNode): raw_tokens = unparse(params) else: raw_tokens = [params] len_carrots = (raw_tokens[-1].ndx + len(raw_tokens[-1].value)) - raw_tokens[0].ndx if len(raw_tokens) > 1 else len(raw_tokens[0].value) line, ndx = get_position(raw_tokens[0].ndx) message += ' [line:%d position:%d]' % (line, ndx) message += '\n\n%s' % getln(line, ndx, len_carrots) raise SyCloneRecoverableError(message)
def generate_delete(stmt): # hold Identifiers to be put in final StatementNode identifiers = [] # hold the initial variable names given variables = [stmt.content[1]] # if there are multiple variables if isinstance(stmt.content[-1], ASTNode): # iterate through unparsed statement and add all identifiers for item in unparse(stmt.content[-1]): if item.type == 'IDENTIFIER': variables.append(item) # iterate through generated variable list for item in variables: # look up to get identifier sym = util.symbol_table.look_up(item.value) # attempt to delete, fail if impossible if not util.symbol_table.delete(item.value): errormodule.throw('semantic_error', 'Unable to delete non-existent symbol', item) # add identifiers to list identifiers.append(Identifier(sym.name, sym.data_type, Modifiers.CONSTANT in sym.modifiers)) return StatementNode('Delete', *identifiers)
def generate_assign_var(assign_var): # generate identifier def generate_id_type(id_type): # handle this token # uses Module.get_instance() if id_type.content[0].type == 'THIS': return get_instance() # otherwise look up symbol and return identifier else: sym = util.symbol_table.look_up(id_type.content[0].value) if not sym: errormodule.throw('semantic_error', 'Variable \'%s\' not defined' % id_type.content[0].value, id_type) return Identifier(sym.name, sym.data_type, Modifiers.CONSTANT in sym.modifiers) # if there is single ASTNode, assume the it is id_types if isinstance(assign_var.content[0], ASTNode): # return generate id type return generate_id_type(assign_var.content[0]) # there is a dereference operator else: # check if there is an id type after the dereference operator if isinstance(assign_var.content[-1].content[0], ASTNode): root = generate_id_type(assign_var.content[-1].content[0]) # otherwise generate sub var else: root = generate_assign_var(assign_var.content[-1].content[1]) # check for trailer if len(assign_var.content[-1].content) > 3: root = add_trailer(root, assign_var.content[-1].content[2]) # calculate the dereference count deref_count = 1 if len(assign_var.content) == 2 else len(unparse(assign_var.content[1])) + 1 # check for non-pointer dereference if deref_count > root.data_type.pointers: errormodule.throw('semantic_error', 'Unable to dereference a non-pointers', assign_var) dt = copy(root.data_type) dt.pointers -= deref_count return ExprNode('Dereference', dt, deref_count, root)
def compile_parameters(param_ast): params = [] expr = '' if not isinstance(param_ast, ASTNode): return params for item in param_ast.content: if isinstance(item, ASTNode): if item.name == 'expr': expr = item elif item.name == 'named_param': ue = unparse(expr) if len(ue) > 1 or isinstance( ue[0], ASTNode) or ue[0].type != 'IDENTIFIER': errormodule.throw( 'semantic_error', 'Invalid parameter name', param_ast. content[0 if isinstance(param_ast.content[0], ASTNode ) else 1]) expr = (ue[0].value, generate_expr(item.content[1])) elif item.name == 'n_param': params += compile_parameters(item) return [generate_expr(expr) if isinstance(expr, ASTNode) else expr ] + params
def generate_unary_atom(u_atom): # generate hold atom atom = generate_atom(u_atom.content[-1]) if len(u_atom.content) > 1: # check for packages if isinstance(atom, Package): errormodule.throw('semantic_error', 'Unable to apply operator to package', u_atom) return # check for tuples if isinstance(atom.data_type, types.Tuple): errormodule.throw('semantic_error', 'Unable to apply operator to multiple values', u_atom) return # check data type literal if isinstance(atom.data_type, types.DataTypeLiteral): errormodule.throw('semantic_error', 'Unable to apply operator to Data Type literal', u_atom) return # check for template if isinstance(atom.data_type, types.Template): errormodule.throw('semantic_error', 'Unable to apply operator to template', u_atom) return prefix = u_atom.content[0].content[0] # handle sine change if prefix.type == '-': # test for numericality if types.numeric(atom.data_type): # handle modules if isinstance(atom.data_type, types.CustomType): invert_method = modules.get_property( atom.data_type, '__invert__') return ExprNode('Call', invert_method.data_type.return_type, invert_method) # change the sine of an element else: return ExprNode('ChangeSine', atom.data_type, atom) else: # throw error errormodule.throw( 'semantic_error', 'Unable to change sine on non-numeric type.', u_atom) elif prefix.type == 'AMP': dt = copy(atom.data_type) # create pointer dt.pointers += 1 # reference pointer return ExprNode('Reference', dt, atom) # handle deref op elif prefix.type == '*': do = len(unparse(u_atom.content[0].content[1])) + 1 if len( u_atom.content[0].content) > 1 else 1 # handle pointer error # < because that means there is more dereferencing than there are references to dereference if atom.data_type.pointers < do: errormodule.throw('semantic_error', 'Unable to dereference a non-pointer', u_atom.content[0]) elif isinstance(atom.data_type, types.VoidPointer): if atom.data_type.pointers <= do: errormodule.throw('semantic_error', 'Unable to dereference void pointer', u_atom.content[0]) vp = copy(atom.data_type) vp.pointers -= 1 return ExprNode('Dereference', vp, do, atom) else: dt = copy(atom.data_type) dt.pointers -= do # return dereference with count return ExprNode('Dereference', dt, do, atom) else: return atom
def generate_type(ext): pointers = 0 # handle std types (from extension) if ext.name == 'types': if ext.content[0].name == 'deref_op': # extract data type pointers pointers = len(unparse(ext.content[0])) # update ext to simple types ext = ext.content[-1] # selects last element (always simple types) # if it is token, assume array, list or dict if isinstance(ext.content[0], Token): # assume array if ext.content[0].type == 'ARRAY_TYPE': # ext.content[1].content[1] == pure_types -> array_modifier -> types et = generate_type(ext.content[1].content[1]) # extract count value # ext.content[1].content[1] == pure_types -> array_modifiers -> expr error_ast = ext.content[1].content[3] try: count = get_array_bound( generate_expr(ext.content[1].content[3])) except IndexError: errormodule.throw('semantic_error', 'Index out of range', error_ast) return if not count and count != 0: errormodule.throw('semantic_error', 'Non-constexpr array bound', error_ast) elif type(count) == bool: errormodule.throw('semantic_error', 'Invalid value for array bound', error_ast) try: _ = count < 0 count = float(count) except (ValueError, TypeError): errormodule.throw('semantic_error', 'Invalid value for array bound', error_ast) if not count.is_integer(): errormodule.throw('semantic_error', 'Invalid value for array bound', error_ast) elif count < 1: errormodule.throw('semantic_error', 'Invalid value for array bound', error_ast) return types.ArrayType(et, int(count), pointers) # assume list elif ext.content[0].type == 'LIST_TYPE': # ext.content[1].content[1] == pure_types -> list_modifier -> types return types.ListType(generate_type(ext.content[1].content[1]), pointers) # assume function elif ext.content[0].type in {'FUNC', 'ASYNC'}: params, return_types = None, None for item in ext.content[1].content: if not isinstance(item, Token): if item.name == 'func_params_decl': params = generate_parameter_list(item) elif item.name == 'rt_type': return_types = get_return_from_type(item) return types.Function(params, return_types, 0, ext.content[0].value == 'ASYNC', False) # assume dict else: # ext.content[1].content[1] == pure_types -> dict_modifier -> types kt, vt = generate_type(ext.content[1].content[1]), generate_type( ext.content[1].content[3]) # check mutability if types.mutable(kt): errormodule.throw('semantic_error', 'Invalid key type for dictionary', ext.content[1].content[1]) # compile dictionary type return types.MapType(kt, vt, pointers) else: if ext.content[0].name == 'pure_types': # data type literal if ext.content[0].content[0].type == 'DATA_TYPE': return types.DataTypeLiteral(types.DataTypes.DATA_TYPE) # return matched pure types return types.DataType({ 'INT_TYPE': types.DataTypes.INT, 'BOOL_TYPE': types.DataTypes.BOOL, 'BYTE_TYPE': types.DataTypes.BYTE, 'FLOAT_TYPE': types.DataTypes.FLOAT, 'LONG_TYPE': types.DataTypes.LONG, 'COMPLEX_TYPE': types.DataTypes.COMPLEX, 'STRING_TYPE': types.DataTypes.STRING, 'CHAR_TYPE': types.DataTypes.CHAR, 'OBJECT_TYPE': types.OBJECT_TEMPLATE, }[ext.content[0].content[0].type], 0) else: # get root symbol name = ext.content[0].content[0].value if name.type == 'THIS': sym = modules.get_instance() else: sym = util.symbol_table.look_up(name) # hold previous symbol ASTNode for error messages prev_sym = ext.content[0].content[0] # extract outer symbols if necessary if len(ext.content[0].content) > 1: content = ext.content[0].content[1:] for item in content: if isinstance(item, Token): if item.type == 'IDENTIFIER': # make sure symbol is a custom type if not isinstance(sym, types.CustomType): errormodule.throw( 'semantic_error', 'Object is not a valid is a data type', prev_sym) identifier = item.value # get member for modules if sym.data_type.data_type == types.DataTypes.MODULE: # get and check property prop = modules.get_property( sym.data_type, identifier) if not prop: errormodule.throw( 'semantic_error', 'Object has no member \'%s\'' % identifier, item) # update previous symbol prev_sym = item # update symbol sym = prop # invalid get member on interfaces elif sym.data_type.data_type == types.DataTypes.INTERFACE: errormodule.throw( 'semantic_error', '\'.\' is not valid for this object', item) # assume struct or enum else: for member in sym.data_type.members: # if there is a match, extract value if member.name == identifier: prev_sym = item sym = member # break to prevent else condition break # if there is no match, throw error else: errormodule.throw( 'semantic_error', 'Object has no member \'%s\'' % identifier, item) # continue recursive for loop elif item.name == 'dot_id': content.extend(item.content) # final check for invalid data types if not isinstance(sym, types.CustomType): errormodule.throw('semantic_error', 'Object is not a valid is a data type', prev_sym) # add instance marker if necessary if sym.data_type.data_type != types.DataTypes.INTERFACE: sym.data_type.instance = True return sym.data_type