def generate_logical(logical): # if it is a comparison, pass it on to the next generator if logical.name == 'comparison': return generate_comparison(logical) # unpack tree if it exists if len(logical.content) > 1: # hold the unpacked tree unpacked_tree = logical.content[:] # semi-recursive for loop to unpack tree for item in unpacked_tree: # if it is an ASTNode if isinstance(item, ASTNode): # and falls into the 'n' categories if item.name.startswith('n'): # unpack it unpacked_tree += unpacked_tree.pop().content # root holds the next level downward, op = operator being used root, op = generate_logical(unpacked_tree.pop(0)), None # iterate through unpacked tree for item in unpacked_tree: # main tree if isinstance(item, ASTNode): # get next comparison tree if it is a comparison otherwise get next logical tree tree = generate_comparison( item) if item.name == 'comparison' else generate_logical( item) # if both are simple data types if isinstance(tree.data_type, types.DataType) and isinstance( root.data_type, types.DataType): # check booleans and generate boolean operators if tree.data_type.data_type == types.DataTypes.BOOL and tree.data_type.pointers == 0 and \ root.data_type.data_type == types.DataTypes.BOOL and root.data_type.pointers == 0: root = ExprNode( op, types.DataType(types.DataTypes.BOOL, 0), root, tree) continue # generate bitwise operators else: # extract dominant type and if there is not one, throw error dom = types.dominant(root.data_type, tree.data_type) if dom: if not types.coerce( types.DataType(types.DataTypes.INT, 0), dom): errormodule.throw( 'semantic_error', 'Unable to apply bitwise %s to object' % op.lower(), logical) root = ExprNode('Bitwise' + op, dom, root, tree) else: if not types.coerce( types.DataType(types.DataTypes.INT, 0), tree.data_type): errormodule.throw( 'semantic_error', 'Unable to apply bitwise %s to object' % op.lower(), logical) root = ExprNode('Bitwise' + op, tree.data_type, root, tree) # handle operator overloading elif isinstance(root.data_type, types.CustomType): # get and check method method = modules.get_property(tree.data_type, '__%s__' % op.lower()) functions.check_parameters(method, [tree], item) if method: root = ExprNode('Call', method.data_type.return_type, method, tree) else: errormodule.throw( 'semantic_error', 'Object has no method \'__%s__\'' % op.lower(), logical) else: errormodule.throw( 'semantic_error', 'Unable to apply bitwise operator to object', logical) # only token is operator else: name = item.type.lower() op = name[0].upper() + name[1:] return root # otherwise recur to next level down else: return generate_logical(logical.content[0])
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_arithmetic(ari): # names of overload methods method_names = { '+': '__add__', '-': '__sub__', '*': '__mul__', '/': '__div__', '%': '__mod__', '^': '__pow__' } # unpack tree in operator sets unpacked_tree = ari.content[:] while unpacked_tree[-1].name in {'add_sub_op', 'mul_div_mod_op', 'exp_op'}: unpacked_tree += unpacked_tree.pop().content # root = base tree, op = operator, tree = resulting tree root, op, tree = None, None, None for item in unpacked_tree: # ast => expression if isinstance(item, ASTNode): # if there is an operator, generate arithmetic tree if op: # generate next value val = generate_unary_atom( item ) if item.name == 'unary_atom' else generate_arithmetic(item) # check operands and extract data type dt = check_operands(root.data_type, val.data_type, op, ari) if tree: # operator overloading if isinstance(tree.data_type, types.CustomType): # convert to method name name = method_names[op] # get method and check parameters method = modules.get_property(tree.data_type, name) # assume method exists functions.check_parameters(method.data_type, [val], item) tree = ExprNode('Call', method.data_type.return_type, method, [tree]) # add arguments to previous tree (allow for multiple items) elif tree.name == op: tree.arguments.append(val) # create expression node from tree else: tree = ExprNode(op, dt, tree, val) # create root tree else: # operator overloading if isinstance(root.data_type, types.CustomType): # convert to method name name = method_names[op] # get method and check parameters method = modules.get_property(root.data_type, name) # assume method exists functions.check_parameters(method.data_type, [val], item) tree = ExprNode('Call', method.data_type.return_type, method, [root]) # default generation else: tree = ExprNode(op, dt, root, val) op, root, = None, tree # otherwise, generate root else: root = generate_unary_atom( item ) if item.name == 'unary_atom' else generate_arithmetic(item) # token => operator else: op = item.value # ensure tree is initialized if not tree: tree = root return tree
def generate_shift(shift): # if there is a shift performed if len(shift.content) > 1: # extract operator-expr list unpacked_tree = shift.content[:] while unpacked_tree[-1].name == 'n_shift': unpacked_tree += unpacked_tree.pop().content # get first expression root = generate_arithmetic(unpacked_tree.pop(0)) # check for overload flag overload = False # check root data type if not isinstance(root.data_type, types.DataType): if isinstance(root.data_type, types.CustomType): overload = True else: errormodule.throw( 'semantic_error', 'Invalid type for left operand of binary shift', shift.content[0]) # operator used op = '' for item in unpacked_tree: # ast node => arithmetic expr if isinstance(item, ASTNode): # generate next tree element tree = generate_arithmetic(item) # check for overloads if overload: method = modules.get_property(root.data_type, '__%s__' % op.lower()) if not method: errormodule.throw( 'semantic_error', 'Invalid type for left operand of binary shift', shift.content[0]) functions.check_parameters(method.data_type, [tree], item) overload = isinstance(method.data_type.return_type, types.CustomType) root = ExprNode('Call', method.data_type.return_type, method, [tree]) # type check element if isinstance(tree.data_type, types.DataType): # ensure it is an integer (binary shifts can only be continued by integers) if tree.data_type.data_type == types.DataTypes.INT and tree.data_type.pointers == 0: root = ExprNode(op, root.data_type, root, tree) continue errormodule.throw( 'semantic_error', 'Invalid type for right operand of binary shift', item) # token => operator else: if item.type == '<<': op = 'Lshift' elif item.type == '>>': op = 'ARshift' else: op = 'LRshift' return root # otherwise, pass on to arithmetic parser else: return generate_arithmetic(shift.content[0])
def generate_comparison(comparison): # generate shift if it is a shift tree (because the comparison generator is recursive) if comparison.name == 'shift': return generate_shift(comparison) # evaluate comparison if there is one if len(comparison.content) > 1: # generate inversion operator if comparison.name == 'not': # generate base tree to invert tree = generate_shift(comparison.content[1]) # check for data types if isinstance(tree.data_type, types.DataType): # unable to ! pointers if tree.data_type.pointers > 0: errormodule.throw( 'semantic_error', 'The \'!\' operator is not applicable to pointer', comparison) # generate normal not operator return ExprNode('Not', tree.data_type, tree) # generate overloaded not operator elif isinstance(tree.data_type, types.CustomType): # get and check overloaded not method not_method = modules.get_property(tree.data_type, '__not__') functions.check_parameters(not_method, tree, comparison) if not_method: return ExprNode('Call', not_method.data_type.return_type, not_method, tree) else: errormodule.throw('semantic_error', 'Object has no method \'__not__\'', comparison) else: errormodule.throw( 'semantic_error', 'The \'!\' operator is not applicable to object', comparison) # otherwise generate normal comparison operator else: # comparison op method dictionary comparison_methods = { '<=': '__lteq__', '>=': '__gteq__', "<": '__lt__', '>': '__gt__', '==': '__eq__', '!=': '__neq__', '===': '__seq__', '!==': '__sneq__' } # unpack tree into expressions and operators unpacked_tree = comparison.content[:] for item in unpacked_tree: if item.name == 'n_comparison': unpacked_tree += unpacked_tree.pop().content # root = first element in unpacked tree, op = operator root, op = generate_comparison(unpacked_tree.pop(0)), None for item in unpacked_tree: # all elements are ASTs # not is base expression if item.name == 'not': # extract next operator tree n_tree = generate_comparison(item) # check for overloads if isinstance(root.data_type, types.CustomType): method = modules.get_property(root.data_type, comparison_methods[op]) if not method: if op in {'<=', '>=', '<', '>'}: errormodule.throw( 'semantic_error', 'Unable to use numeric comparison with non-numeric type', comparison) else: functions.check_parameters(method.data_type, [n_tree], comparison) root = ExprNode('Call', method.data_type.return_type, method, [n_tree]) # check numeric comparison if op in {'<=', '>=', '<', '>'}: # check invalid overloads if isinstance(n_tree.data_type, types.CustomType): errormodule.throw( 'semantic_error', 'Invalid type match up for numeric comparison', comparison) if types.numeric(n_tree.data_type) and types.numeric( root.data_type): root = ExprNode( op, types.DataType(types.DataTypes.BOOL, 0), root, n_tree) else: errormodule.throw( 'semantic_error', 'Unable to use numeric comparison with non-numeric type', comparison) # generate standard comparison elif op in {'==', '!=', '===', '!=='}: root = ExprNode( op, types.DataType(types.DataTypes.BOOL, 0), root, n_tree) # if it is not a base expression, it is an operators elif item.name == 'comparison_op': op = item.content[0].value return root # otherwise recur else: return generate_comparison(comparison.content[0])
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