def call(self, obj, args): arrayType = type(None) isArgArray = _isType(args, RocketArray) nin_lexeme = obj.KSL[1][_TokenType.NIN.value] if isArgArray: arrayType = args.arrayType else: arrayType = type(args[0]) # if a single arg is given we assume it to be the size of the Array if len(args) == 1 and not (isArgArray): if _isType(args[0], _number.RocketInt): arrayType = type(None) return RocketArray([None for i in range(args[0].value)], arrayType, nin_lexeme) else: raise _RuntimeError("Array', 'Array size must be Int.") if len(args) > 1 and not (isArgArray): # Check that all elms are of the same type if not _isAllSameType(args, arrayType): raise _RuntimeError('Array', "Array elements must be adjacent types.") return RocketArray(args, arrayType, nin_lexeme) if not isArgArray else args
def call(interpreter, args): if self.notEmpty(): if args[0].value in self.value: return String().call(self, [self.value.index(args[0].value)]) else: raise _RuntimeError('String', "IndexError: Item not in string") else: raise _RuntimeError('String', "IndexError: cannot index from an empty string")
def call(interpreter, args): if self.notEmpty(): for i in range(len(self.elements)): if args[0].value == self.elements[i].value: return _number.Int().call(self, [i]) raise _RuntimeError('List', "IndexError: Item not in list") else: raise _RuntimeError('List', "IndexError: cannot index from an empty list")
def visitImportStmt(self, stmt: _Import): import_lexeme = self.KSL[1][_TokenType.IMPORT.value] # TODO: add more native modules native_modules = [ 'math' # Note: even this module isn't done ] if len(stmt.modules) == 0: raise _RuntimeError( import_lexeme, f"{import_lexeme} statement requires atleast one module name.", False) for module in stmt.modules: if (module.type == _TokenType.IDENTIFIER) and (module.lexeme in native_modules): # read and execute sorce file contents = '' # Get exec base home basehome = _os.path.dirname( _os.path.dirname(_os.path.realpath(__file__))) # Assemble native module path filename = _os.path.join( basehome, "native/modules/" + module.lexeme + '.rckt') stmts = _importCodeStmts(filename, self.KSL) self.interpret(stmts) else: try: # read and execute sorce file contents = '' # This way the user can specify both './path/to/module.rckt' and './path/to/module' are valid filename = ( module.lexeme + '.rckt') if module.lexeme.split( _os.path.extsep)[-1] != 'rckt' else module.lexeme stmts = _importCodeStmts(filename, self.KSL) self.interpret(stmts) except FileNotFoundError: raise _RuntimeError( import_lexeme, f"No native or local module named '{module.lexeme}'.", False) return None
def visitGetExpr(self, expr: _Get): object = self.evaluate(expr.object) if isinstance(object, _RocketInstance): return object.get(expr.name) # Another special check for datatypes if hasattr(object, 'nature'): try: return object.get(expr.name) except Exception as err: raise _RuntimeError(err.token, err.msg) else: raise _RuntimeError(expr.name, "Only instances have properties.", False)
def visitClassStmt(self, stmt: _Class): super_lexeme = self.KSL[1][_TokenType.SUPER.value] this_lexeme = self.KSL[1][_TokenType.THIS.value] superclass = None if (stmt.superclass != None): superclass = self.evaluate(stmt.superclass) if not isinstance(superclass, _RocketClass): raise _RuntimeError(stmt.superclass.name, "Superclass must be a class.", False) self.environment.define(stmt.name.lexeme, None) if stmt.superclass != None: self.environment = _Environment(self.environment) self.environment.define(super_lexeme, superclass) methods = {} for method in stmt.methods: function = _RocketFunction(method, self.environment, method.name.lexeme.__eq__("init"), this_lexeme) methods[method.name.lexeme] = function class_ = _RocketClass(stmt.name.lexeme, superclass, methods) if (superclass != None): self.environment = self.environment.enclosing self.environment.assign(stmt.name, class_) return None
def call(interpreter, args): index = args[0].value if index >= len(self.elements): raise _RuntimeError('List', "IndexError: list index out of range") return self.elements[index]
def call(interpreter, args): if (self.arrayType != _number.RocketInt) and ( self.arrayType != _number.RocketFloat): raise _RuntimeError( self, "Can only perform operation on number Arrays.") if len(self.elements) == 0: return _RuntimeError(self, "Can't get mean of empty an Array.") sum = 0 for i in range(0, len(self.elements)): sum += self.elements[i].value return _number.Float().call(self, [sum / len(self.elements)])
def call(interpreter, args): if isinstance(args[0], RocketList): # we return the mutation return List().call(self, self.elements + args[0].elements) else: raise _RuntimeError('List', "IndexError: can only concatenate 'List' native type with another 'List'.")
def call(interpreter, args): index = args[0].value if index >= len(self.value): raise _RuntimeError('String', "IndexError: string index out of range") return String().call(self, [self.value[index]])
def call(interpreter, args): if self.notEmpty(): last = self.elements[-1] self.elements.remove(last) return last else: raise _RuntimeError('List', "IndexError: cannot pop empty list")
def call(interpreter, args): if self.notEmpty(): if args[0].value in self.value: return _boolean.Bool().call(self, [True]) else: return _boolean.Bool().call(self, [False]) else: raise _RuntimeError('String', "IndexError: cannot index from an empty string")
def call(interpreter, args): if self.notEmpty(): for item in self.elements: args[0].call(interpreter, [item]) else: raise _RuntimeError( 'Array', "IndexError: cannot run function on an empty list")
def call(interpreter, args): if (self.arrayType != _number.RocketInt) and ( self.arrayType != _number.RocketFloat): raise _RuntimeError( self, "Can only perform operation on number Arrays.") if len(self.elements) == 0: return _RuntimeError(self, "Can't get max of empty an Array.") max = self.elements[0].value for i in range(1, len(self.elements)): if max < self.elements[i].value: max = self.elements[i].value return _number.Int().call(self, [max])
def call(interpreter, args): if self.notEmpty(): removed_index = -1 for i in range(len(self.elements) - 1): if args[0].value == self.elements[i].value: self.elements.remove(self.elements[i]) removed_index = i if removed_index == -1: raise _RuntimeError('List', "IndexError: Item not in list") else: return _number.Int().call(self, [removed_index]) else: raise _RuntimeError('List', "IndexError: cannot remove items from an empty list")
def visitSetExpr(self, expr: _Set): obj = self.evaluate(expr.object) if hasattr(obj, 'kind'): if ('List' in obj.kind): raise _RuntimeError( 'List', f"Cannot assign external attribute to native datatype 'List'", False) if not isinstance(obj, _RocketInstance): raise _RuntimeError(expr.name, "Only instances have fields.", False) value = self.evaluate(expr.value) obj.set(expr.name, value) return value
def call(interpreter, args): if _isType(args[0], _number.RocketInt): for i in range(len(self.elements)): self.elements[i] = args[0] return self else: raise _RuntimeError(self, "Expected an Int.")
def call(interpreter, args): if self.notEmpty(): if len(self.value) >= 2: return String().call(self, [self.value[0].upper() + self.value[1:]]) else: return String().call(self, [self.value]) else: raise _RuntimeError('String', "IndexError: cannot index from an empty string")
def call(interpreter, args): if self.notEmpty(): endlen = len(args[0].value) index = -(endlen) if args[0] == self.value[index:]: return _boolean.Bool().call(self, [True]) else: return _boolean.Bool().call(self, [False]) else: raise _RuntimeError('String', "IndexError: cannot index from an empty string")
def call(interpreter, args): if self.notEmpty(): for i in range(len(self.elements)): if args[0].value == self.elements[i].value: return _boolean.Bool().call(self, [True]) return _boolean.Bool().call(self, [False]) else: raise _RuntimeError('List', "IndexError: cannot index from an empty list")
def call(self, obj, args): if isNumber(args[0]): value = args[0] if (hasattr(args[0], 'value')): value = args[0].value return RocketInt(int(float(value)) if _isType(args[0], _string.RocketString) else int(value)) raise _RuntimeError(obj, f"Type Mismatch: Cannot convert {args[0].kind} to Int.")
def call(interpreter, args): if isinstance(args[0], RocketString): # We do not internally edit it, instead its returned # self.value = self.value + new_list.elements text = args[0].value return String().call(self, [self.value + text]) else: raise _RuntimeError('String', "IndexError: can only concatenate 'String' native type with another 'String'.")
def call(self, obj, args): if isNumber(args[0]): value = args[0] if hasattr(args[0], 'value'): value = args[0].value return RocketFloat(float(value)) else: raise _RuntimeError(obj, f"'Float' accepts either Int or Float as an argument.")
def visitConstStmt(self, stmt: _Const): value = self.evaluate(stmt.initializer) # check for variable before definition to avoid passing in 'const' redefinitions if self.environment.constExists(stmt.name): raise _RuntimeError(stmt.name.lexeme, "Name already used as const.", False) if self.environment.varExists(stmt.name): raise _RuntimeError(stmt.name.lexeme, "Name already used as variable.", False) # stop 'const' re-decl for 'classes' 'functions' elif self.globals.isTaken(stmt.name): raise _RuntimeError( stmt.name.lexeme, "Name already used as 'class' or 'function' name.", False) # Use different decleration function for consts self.environment.decl(stmt.name.lexeme, value)
def call(interpreter, args, inc=False): if inc: if args[0].value >= len(self.elements) or args[1].value >= len(self.elements): raise _RuntimeError('List', "IndexError: list index out of range") # Special case if (args[0].value >= args[1].value): return List().call(self, []) else: return List().call(self, self.elements[args[0].value:args[1].value]) return List().call(self, self.elements[args[0].value:])
def call(interpreter, args, inc=False): if inc: if (args[0].value >= len(self.value)) or (args[1].value >= len(self.value)): raise _RuntimeError('String', "IndexError: string index out of range") # Special case if (args[0].value >= args[1].value): String().call(self, ['']) else: return String().call(self, [self.value[args[0].value:args[1].value]]) return self.value[args[0].value:]
def call(interpreter, args): if (self.arrayType != _number.RocketInt) and ( self.arrayType != _number.RocketFloat): raise _RuntimeError( self, "Can only perform operation on number Arrays.") prod = 1 for i in range(len(self.elements)): prod *= self.elements[i].value self.elements[i].value = prod return self
def checkValidOperands(self, operator: _Token, left: object, right: object): if ((isinstance(left, str)) and (isinstance(right, str))): return if self.is_number(left) and self.is_number(right): return else: # Is comparing strings and numbers really important? # Maybe if you are trying to see if a number id transformed to an 'str'. # But wouldn't you just check the type with 'Type' native func?! raise _RuntimeError( operator.lexeme, "operands must both be either 'strings' or 'numbers'.", False)
def visitVariableExpr(self, expr: _Variable): # NOTE: 'const' variables get retrieved from this call also try: return self.globals.get(expr.name) except: # We try to see if its in either 'envs' # if not we raise the exception for our interpreter to catch try: return self.environment.get(expr.name) except _RuntimeError as err: print(err, file=_sys.stderr) raise _RuntimeError(err.token, err.msg)
def visitVarStmt(self, stmt: _Var): if (stmt.initializer is not None): value = self.evaluate(stmt.initializer) else: value = None # To avoid redifining vars with the same name as consts, functions, or classes if not (self.globals.isTaken(stmt.name)): self.environment.define(stmt.name.lexeme, value) else: raise _RuntimeError( stmt.name.lexeme, "Name already defined as 'class' or 'function'", False)