def make_node(cls, lineno, s, lower, upper): """ Creates a node for a string slice. S is the string expression Tree. Lower and upper are the bounds, if lower & upper are constants, and s is also constant, then a string constant is returned. If lower > upper, an empty string is returned. """ if lower is None or upper is None or s is None: return None if not check_type(lineno, Type.string, s): return None lo = up = None base = NUMBER(api.config.OPTIONS.string_base.value, lineno=lineno) lower = TYPECAST.make_node( gl.SYMBOL_TABLE.basic_types[gl.STR_INDEX_TYPE], BINARY.make_node('MINUS', lower, base, lineno=lineno, func=lambda x, y: x - y), lineno) upper = TYPECAST.make_node( gl.SYMBOL_TABLE.basic_types[gl.STR_INDEX_TYPE], BINARY.make_node('MINUS', upper, base, lineno=lineno, func=lambda x, y: x - y), lineno) if lower is None or upper is None: return None if is_number(lower): lo = lower.value if lo < gl.MIN_STRSLICE_IDX: lower.value = lo = gl.MIN_STRSLICE_IDX if is_number(upper): up = upper.value if up > gl.MAX_STRSLICE_IDX: upper.value = up = gl.MAX_STRSLICE_IDX if is_number(lower, upper): if lo > up: return STRING('', lineno) if s.token == 'STRING': # A constant string? Recalculate it now up += 1 st = s.value.ljust(up) # Procrustean filled (right) return STRING(st[lo:up], lineno) # a$(0 TO INF.) = a$ if lo == gl.MIN_STRSLICE_IDX and up == gl.MAX_STRSLICE_IDX: return s return cls(s, lower, upper, lineno)
def make_node(cls, lineno, operator, operand, func=None, type_=None): """ Creates a node for a unary operation. E.g. -x or LEN(a$) Parameters: -func: lambda function used on constant folding when possible -type_: the resulting type (by default, the same as the argument). For example, for LEN (str$), result type is 'u16' and arg type is 'string' """ assert type_ is None or isinstance(type_, SymbolTYPE) if func is not None: # Try constant-folding if is_number(operand): # e.g. ABS(-5) return SymbolNUMBER(func(operand.value), lineno=lineno) elif is_string(operand): # e.g. LEN("a") return SymbolSTRING(func(operand.text), lineno=lineno) if type_ is None: type_ = operand.type_ if operator == 'MINUS': if not type_.is_signed: type_ = type_.to_signed() operand = SymbolTYPECAST.make_node(type_, operand, lineno) elif operator == 'NOT': type_ = TYPE.ubyte return cls(operator, operand, lineno, type_)
def visit_IF(self, node): expr_ = (yield ToVisit(node.children[0])) then_ = (yield ToVisit(node.children[1])) else_ = (yield ToVisit(node.children[2])) if len( node.children) == 3 else self.NOP if self.O_LEVEL >= 1: if chk.is_null(then_, else_): api.errmsg.warning_empty_if(node.lineno) yield self.NOP return block_accessed = chk.is_block_accessed( then_) or chk.is_block_accessed(else_) if not block_accessed and chk.is_number( expr_): # constant condition if expr_.value: # always true (then_) yield then_ else: # always false (else_) yield else_ return if chk.is_null(else_) and len(node.children) == 3: node.children.pop() # remove empty else yield node return for i in range(len(node.children)): node.children[i] = (expr_, then_, else_)[i] yield node
def make_node(cls, id_, arglist, lineno): """ Creates an array access. A(x1, x2, ..., xn) """ assert isinstance(arglist, SymbolARGLIST) variable = gl.SYMBOL_TABLE.access_array(id_, 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_) # Returns the variable entry and the node return cls(variable, arglist, lineno)
def offset(self): """ If this is a constant access (e.g. A(1)) return the offset in bytes from the beginning of the variable in memory. Otherwise, if it's not constant (e.g. A(i)) returns None """ offset = 0 # Now we must typecast each argument to a u16 (POINTER) type # i is the dimension ith index, b is the bound for i, b in zip(self.arglist, self.entry.bounds): tmp = i.children[0] if is_number(tmp) or is_const(tmp): if offset is not None: offset = offset * b.count + tmp.value else: offset = None break if offset is not None: offset = TYPE.size(gl.SIZE_TYPE) + TYPE.size(gl.BOUND_TYPE) * len( self.arglist) + offset * self.type_.size return offset
def visit_WHILE(self, node): expr_ = (yield node.children[0]) body_ = (yield node.children[1]) if self.O_LEVEL >= 1: if chk.is_number( expr_ ) and not expr_.value and not chk.is_block_accessed(body_): yield self.NOP return for i, child in enumerate((expr_, body_)): node.children[i] = child yield node
def make_node(cls, lineno, fname, func=None, type_=None, *operands): """ Creates a node for a unary operation. E.g. -x or LEN(a$) Parameters: -func: function used on constant folding when possible -type_: the resulting type (by default, the same as the argument). For example, for LEN (str$), result type is 'u16' and arg type is 'string' """ if func is not None and len(operands) == 1: # Try constant-folding if is_number(operands[0]) or is_string( operands[0]): # e.g. ABS(-5) return SymbolNUMBER(func(operands[0].value), type_=type_, lineno=lineno) return cls(lineno, fname, type_, *operands)
def visit_FOR(self, node): from_ = (yield node.children[1]) to_ = (yield node.children[2]) step_ = (yield node.children[3]) body_ = (yield node.children[4]) if self.O_LEVEL > 0 and chk.is_number( from_, to_, step_) and not chk.is_block_accessed(body_): if from_ > to_ and step_ > 0: yield self.NOP return if from_ < to_ and step_ < 0: yield self.NOP return for i, child in enumerate((from_, to_, step_, body_), start=1): node.children[i] = child yield node
def visit_CONST(self, node): if chk.is_number(node.expr) or chk.is_const(node.expr): yield node.expr else: yield node
def make_node(cls, new_type, node, lineno): """ Creates a node containing the type cast of the given one. If new_type == node.type, then nothing is done, and the same node is returned. Returns None on failure (and calls syntax_error) """ assert isinstance(new_type, SymbolTYPE) # None (null) means the given AST node is empty (usually an error) if node is None: return None # Do nothing. Return None assert isinstance(node, Symbol), '<%s> is not a Symbol' % node # The source and dest types are the same 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: syntax_error( lineno, 'Cannot convert string to a value. ' 'Use VAL() function') return None # Converting from string to number is done by STR if new_type == STRTYPE: syntax_error( lineno, 'Cannot convert value to string. ' 'Use STR() function') return None # If the given operand is a constant, perform a static typecast if is_CONST(node): node.expr = cls(new_type, node.expr, lineno) return node if not is_number(node) and not is_const(node): return cls(new_type, node, lineno) # It's a number. So let's convert it directly if is_const(node): node = SymbolNUMBER(node.value, node.lineno, node.type_) if new_type.is_basic and not TYPE.is_integral( new_type): # not an integer node.value = float(node.value) else: # It's an integer new_val = (int(node.value) & ((1 << (8 * new_type.size)) - 1) ) # Mask it if node.value >= 0 and node.value != new_val: errmsg.warning_conversion_lose_digits(node.lineno) node.value = new_val elif node.value < 0 and (1 << (new_type.size * 8)) + \ node.value != new_val: # Test for positive to negative coercion errmsg.warning_conversion_lose_digits(node.lineno) node.value = new_val - (1 << (new_type.size * 8)) node.type_ = new_type return node
def make_node(cls, operator, left, right, lineno, func=None, type_=None): """ Creates a binary node for a binary operation, e.g. A + 6 => '+' (A, 6) in prefix notation. Parameters: -operator: the binary operation token. e.g. 'PLUS' for A + 6 -left: left operand -right: right operand -func: is a lambda function used when constant folding is applied -type_: resulting type (to enforce it). If no type_ is specified the resulting one will be guessed. """ if left is None or right is None: return None a, b = left, right # short form names # Check for constant non-numeric operations c_type = common_type(a, b) # Resulting operation type or None if c_type: # there must be a common type for a and b if is_numeric(a, b) and (is_const(a) or is_number(a)) and \ (is_const(b) or is_number(b)): if func is not None: a = SymbolTYPECAST.make_node(c_type, a, lineno) # ensure type b = SymbolTYPECAST.make_node(c_type, b, lineno) # ensure type return SymbolNUMBER(func(a.value, b.value), type_=type_, lineno=lineno) if is_static(a, b): a = SymbolTYPECAST.make_node(c_type, a, lineno) # ensure type b = SymbolTYPECAST.make_node(c_type, b, lineno) # ensure type return SymbolCONST(cls(operator, a, b, lineno, type_=type_, func=func), lineno=lineno) if operator in ('BNOT', 'BAND', 'BOR', 'BXOR', 'NOT', 'AND', 'OR', 'XOR', 'MINUS', 'MULT', 'DIV', 'SHL', 'SHR') and \ not is_numeric(a, b): syntax_error(lineno, 'Operator %s cannot be used with STRINGS' % operator) return None if is_string(a, b) and func is not None: # Are they STRING Constants? if operator == 'PLUS': return SymbolSTRING(func(a.value, b.value), lineno) return SymbolNUMBER(int(func(a.text, b.text)), type_=TYPE.ubyte, lineno=lineno) # Convert to u8 (boolean) if operator in ('BNOT', 'BAND', 'BOR', 'BXOR'): if TYPE.is_decimal(c_type): c_type = TYPE.long_ if a.type_ != b.type_ and TYPE.string in (a.type_, b.type_): c_type = a.type_ # Will give an error based on the fist operand if operator not in ('SHR', 'SHL'): a = SymbolTYPECAST.make_node(c_type, a, lineno) b = SymbolTYPECAST.make_node(c_type, b, lineno) if a is None or b is None: return None if type_ is None: if operator in ('LT', 'GT', 'EQ', 'LE', 'GE', 'NE', 'AND', 'OR', 'XOR', 'NOT'): type_ = TYPE.ubyte # Boolean type else: type_ = c_type return cls(operator, a, b, type_=type_, lineno=lineno)