def array_splice(array, start, end): if not isinstance(array, Array): raise EvaluationError('Cannot splice non-array') if not isinstance(start, int) or not isinstance(end, int): raise EvaluationError('Non-integer indexes passed to splice') # TODO: Make this efficient return Array(array.values[start:end])
def call(self, arguments, interpereter): # TODO: setup scope if len(arguments) > 128: raise EvaluationError('No more than 128 \ arguments may be passed to a function at once.') if self.variadic: if len(arguments) < len(self.parameters) - 1: raise EvaluationError( 'Incorrect number of arguments for function.') else: if len(arguments) != len(self.parameters): raise EvaluationError( 'Incorrect number of arguments for function.') tp = tuple(arguments) if tp not in self.cache: if self.variadic: extra_arguments = arguments[len(self.parameters) - 1:] main_arguments = arguments[:len(self.parameters)] values = { key: value for key, value in zip(self.parameters[:-1], main_arguments) } values[self.parameters[-1]] = Array(extra_arguments) subscope = Scope(self.scope, values) self.cache[tp] = yield from ev(self.expression, subscope, interpereter) else: subscope = Scope(self.scope, { key: value for key, value in zip(self.parameters, arguments) }) self.cache[tp] = yield from ev(self.expression, subscope, interpereter) return self.cache[tp]
def array_join(*items): if len(items) == 0: raise EvaluationError('Cannot join no arrays together.') result = [] for i in items: if not isinstance(i, Array): raise EvaluationError('Cannot call join on non-array') result += i.values return Array(result)
def if_statement(*arguments): if len(arguments) < 3: raise EvaluationError('Too few arguments for if function. Requires 3.') if len(arguments) > 3: raise EvaluationError( 'Too many arguments for if function. Requires 3.') condition, if_true, if_false = arguments if (yield from condition()): return (yield from if_true()) return (yield from if_false())
async def inst_access_array_element(self): index = self.pop() array = self.pop() if not isinstance(array, (Array, Interval)): raise EvaluationError('Cannot access element of non-array object') if not isinstance(index, int): raise EvaluationError('Cannot access non-integer element of an array') if index < 0 or index >= len(array): raise EvaluationError('Attempted to access out-of-bounds element of an array') self.push(array(index))
def make_range(start, end): start = int(start) end = int(end) if not isinstance(start, int): raise EvaluationError('Cannot create range on non-int') if not isinstance(end, int): raise EvaluationError('Cannot create range on non-int') if end < start: raise EvaluationError('Cannot create backwards ranges') return Interval(start, 1, end - start)
async def protected_power(a, b): if a == 0 and b == 0: raise EvaluationError('Cannot raise 0 to the power of 0') sa = float(sympy.Abs(a)) sb = float(sympy.Abs(b)) if sa < 4000 and sb < 20: return a ** b try: return await calculator.crucible.run(_protected_power_crucible, (a, b), timeout=2) except asyncio.TimeoutError: raise EvaluationError('Operation timed out. Perhaps the values were too large?')
def m_choose(*args): if len(args) != 2: raise EvaluationError('choose function requires two arguments') n, k = args try: return calculator.operators.operator_division( calculator.operators.function_factorial(n), calculator.operators.operator_multiply( calculator.operators.function_factorial(k), calculator.operators.function_factorial( calculator.operators.operator_subtract(n, k)))) except: raise EvaluationError( 'Cannot run choose function with arguments {} and {}', n, k)
def call_builtin_function(self, function, arguments, return_to): try: result = function(*arguments) except Exception: # arg = arguments if len(arguments) # pylint: disable=raising-format-tuple if not arguments: raise EvaluationError('Failed to call {} with no arguments.', function) elif len(arguments) == 1: raise EvaluationError('Failed to call {} on {}', function, arguments[0]) else: raise EvaluationError('Failed to call {} on {}', function, arguments) except EvaluationError: raise self.push(result) self.bytes, self.place = return_to
def call(self, arguments, interpereter): if len(arguments) != 1 or not isinstance(arguments[0], int) or not ( 0 <= arguments[0] < len(self.values)): raise EvaluationError( 'Attempted to get non-existent value of array') return self.values[arguments[0]] yield
async def inst_list_extract_rest(self): value = self.pop() if not isinstance( value, (calculator.functions.ListBase, calculator.functions.Array)): raise EvaluationError('Attempted to extract tail of non-list') self.push(value.rest)
async def run_async(self, segment=None, tick_limit=None, error_if_exhausted=False, get_entire_stack=False, assignment_protection_level=None, assignment_auth_level=0): ''' Run some number of ticks. tick_limit - The maximum number of ticks to run. If not specified there is no limit. error_if_exhausted - If True, an error will be thrown if execution is not finished in the specified number of ticks. expect_complete - Deprecated ''' self.assignment_protection_level = assignment_protection_level self.assignment_auth_level = assignment_auth_level self.bytes = segment self.place = 0 if tick_limit is None: while self.head != bytecode.I.END: await self.tick() else: while self.head != bytecode.I.END and tick_limit > 0: tick_limit -= 1 await self.tick() if error_if_exhausted and tick_limit == 0: raise EvaluationError('Execution timed out (by tick count)') if get_entire_stack: return self.stack[1:] return self.top
def internal(*x): try: return f(*x) except Exception: if len(x) == 0: raise EvaluationError( 'Can\'t run {} function with no arguments.'.format(name)) elif len(x) == 1: formatted = format_value(x[0]) raise EvaluationError( 'Can\'t run {} function on value {}'.format( name, formatted)) else: formatted = ', '.join(map(format_value, x)) raise EvaluationError( 'Can\'t run {} function on values ({})'.format( name, formatted))
async def call_function(self, function, arguments, return_to, disable_cache=False, macro_unprepped=False, do_tco=False): if isinstance(function, (BuiltinFunction, Array, Interval, SingularValue)): await self.call_builtin_function(function, arguments, return_to) elif isinstance(function, Function): inspector = FunctionInspector(self, function) need_to_call = True if not disable_cache: cache_key = tuple([function] + arguments) if not inspector.is_macro and cache_key in self.calling_cache: self.push(self.calling_cache[cache_key]) self.bytes, self.place = return_to need_to_call = False if need_to_call: num_parameters = inspector.num_parameters if inspector.is_variadic: if len(arguments) < num_parameters - 1: raise EvaluationError('Not enough arguments for variadic function {}', function) main = arguments[:num_parameters - 1] extra = arguments[num_parameters - 1:] scope_array = main scope_array.append(Array(extra)) new_scope = IndexedScope(function.scope, num_parameters, scope_array) else: if num_parameters != len(arguments): raise EvaluationError('Improper number of arguments for function {}', function) if num_parameters == 0: new_scope = function.scope elif inspector.is_macro and macro_unprepped: wrapped = tuple(map(SingularValue, arguments)) new_scope = IndexedScope(function.scope, num_parameters, wrapped) else: new_scope = IndexedScope(function.scope, num_parameters, arguments) # Remember the current scope if not do_tco: self.push(return_to) self.push(self.current_scope) # For normal functions, the last thing that happens is that the result is # stored in a cache. Need the key in order to do that. self.push(None if disable_cache or inspector.is_macro else cache_key) # Enter the function self.current_scope = new_scope self.bytes = inspector.code_segment self.place = inspector.code_address else: raise EvaluationError('{} is not a function', function) self.place -= 1 # Negate the +1 after this
def switch_statement(*arguments): if len(arguments) % 2 == 0 or len(arguments) < 3: raise EvaluationError( 'Invalid number of arguments for switch expression') for i in range(0, len(arguments) - 2, 2): if (yield from arguments[i]()): return (yield from arguments[i + 1]()) return (yield from arguments[-1]())
def __getitem__(self, key): try: return self.values[key] except KeyError: try: return self.previous[key] except TypeError: raise EvaluationError('Unknown name: {}'.format(key))
def reset(scope, index, depth, permission = 0, protection = None): while depth > 0: scope = scope.superscope depth -= 1 if index < len(scope.slots): _, current_security = scope.slots[index] if current_security > permission: raise EvaluationError('Not permitted to perform this unassignment') scope.slots[index] = DataSlot(None, protection if protection is not None else current_security)
def rolldie(times, faces): if isinstance(times, float): times = int(times) if isinstance(faces, float): faces = int(faces) if not isinstance(times, int) or not isinstance(faces, int): raise EvaluationError('Cannot roll {} dice with {} faces'.format( format_value(times), format_value(faces))) if times < 1: return 0 if times > 1000: raise EvaluationError('Cannot roll more than 1000 dice') if faces < 1: raise EvaluationError('Cannot roll die with less than one face') if isinstance(faces, float) and not faces.is_integer(): raise EvaluationError( 'Cannot roll die with fractional number of faces') return sum(random.randint(1, faces) for i in range(times))
def __init__(self, values): # self.front = [] self.back = values # self.has_ownership = True self.values = list(values) if len(values) > 128: raise EvaluationError( 'Created an array with more than 128 elements. This limitation \ is in place while the feature is in the development.')
def set(scope, index, depth, value, permission = 0, protection = None): while depth > 0: scope = scope.superscope depth -= 1 while len(scope.slots) <= index: scope.slots.append(DataSlot(None, 0)) _, current_security = scope.slots[index] if current_security > permission: raise EvaluationError('Not permitted to perform this assignment') scope.slots[index] = DataSlot(value, protection if protection is not None else current_security)
async def protected_power(use_crucible, a, b): if use_crucible: try: return await calculator.crucible.run(_protected_power_crucible, (a, b), timeout=2) except asyncio.TimeoutError: raise EvaluationError( 'Operation timed out. Perhaps the values were too large?') else: return _protected_power_crucible(a, b)
async def internal(self): right = self.pop() left = self.pop() try: result = bool(await comparator(left, right)) except EvaluationError: raise except Exception: raise EvaluationError('Operation failed on {} and {}', left, right) self.stack[-1] = self.stack[-1] and result self.push(right)
async def internal(self): left = self.pop() right = self.pop() try: result = op(left, right) if is_coroutine: result = await result self.push(result) except EvaluationError: raise except Exception: raise EvaluationError('Operation failed on {} and {}', left, right)
def step(self): # print(self.stack) generator, injector = self.stack[-1] try: new_thing = next(generator) # print(new_thing) if new_thing is not None: if len(self.stack) >= self.limit_stack_size: raise EvaluationError('Stack overflow: too much recursion') self.stack.append(new_thing) except StopIteration as e: injector.append(e.value) self.stack.pop()
async def inst_unr_fac(self): ''' Factorial operator ''' try: original_value = self.pop() argument = original_value # Prevent a burning attack from happening if argument < -2000 or argument > 2000: argument = sympy.Number(float(argument)) result = sympy.factorial(argument) if result == sympy.zoo or result == sympy.oo: raise TypeError except Exception: raise EvaluationError('Cannot run factorial function on {}', original_value) self.push(result)
def array_expand(*arrays): for i in arrays: if not isinstance(i, (Array, ListBase)): raise EvaluationError( 'Cannot expand something that\'s not an array or list') return Expanded(arrays)
def array_length(val): if not isinstance(val, (Array, Interval, ListBase)): raise EvaluationError('Cannot get the length of non-array object') return len(val)
def int_to_glyph(integer): if not isinstance(integer, (int, sympy.Integer)): raise EvaluationError('chr received non-integer') return Glyph(chr(int(integer)))
def glyph_to_int(glyph): if not isinstance(glyph, Glyph): raise EvaluationError('ord received non-glyph') return sympy.Integer(ord(glyph.value))
def evaluate_step(p, scope, it): while True: node_type = p['#'] if node_type == 'number': return convert_number(p['string']) elif node_type == 'bin_op': left = yield from evaluate_step(p['left'], scope, it) right = yield from evaluate_step(p['right'], scope, it) op = OPERATOR_DICT.get(p['operator']) assert op is not None return op(left, right) elif node_type == 'not': value = yield from evaluate_step(p['expression'], scope, it) return 0 if value else 1 elif node_type == 'die': times = (yield from evaluate_step(p['times'], scope, it)) \ if 'times' in p else 1 faces = (yield from evaluate_step(p['faces'], scope, it)) return rolldie(times, faces) elif node_type == 'udie': # THIS IS OLD faces = yield from evaluate_step(p['faces'], scope, it) return rolldie(1, faces) elif node_type == 'uminus': return -(yield from evaluate_step(p['value'], scope, it)) elif node_type == 'function_call': function = yield from evaluate_step(p['function'], scope, it) if not isinstance(function, BaseFunction) and not isinstance( function, Macro): raise EvaluationError('{} is not a function'.format( format_value(function))) # Get the list of AST objects items = p.get('arguments', {'items': []})['items'] args = [] # print(items) for i in items: if isinstance(function, Macro): args.append(wrap_ev(i, scope, it)) else: value = yield from evaluate_step(i, scope, it) if isinstance(value, Expanded): args += list(value) else: args.append(value) return (yield from function.call(args, it)) elif node_type == 'word': name = p['string'].lower() return scope[name] elif node_type == 'factorial': return calculator.operators.function_factorial( (yield from evaluate_step(p['value'], scope, it))) elif node_type == 'assignment': name = p['variable']['string'].lower() value = yield from evaluate_step(p['value'], scope, it) scope[name] = value return value elif node_type == 'statement_list': yield from ev(p['statement'], scope, it) p = p['next'] elif node_type == 'program': result = 0 for i in p['items']: result = yield from ev(i, scope, it) return result elif node_type == 'function_definition': parameters = [ i['string'].lower() for i in p['parameters']['items'] ] function = Function(parameters, p['expression'], scope, p['variadic']) if p['kind'] == '~>': function = Macro(function) return function elif node_type == 'comparison': previous = yield from evaluate_step(p['first'], scope, it) for i in p['rest']: op = i['operator'] current = yield from evaluate_step(i['value'], scope, it) if not OPERATOR_DICT[op](previous, current): return 0 previous = current return 1 elif node_type == 'output': result = yield from evaluate_step(p['expression'], scope, it) print(result) return result else: return None