Exemplo n.º 1
0
def find_next_end_else(line_mgr, start, end_only=False, allow_catch=False):
    """
    Returns the corresponding end-statement or else-statement
    or catch-statement
    for the given clause-opener (which can be an if-statement,
    while-statement, etc.)
    Skips over end and else statements that are part of nested clauses.
    If end_only is set to True, all else statements are passed over.
    """
    i = start
    open_clauses = 0
    while i < len(line_mgr):
        line = line_mgr[i]
        if line == 'end':
            if open_clauses == 0:
                return ['end', i]
            else:
                open_clauses -= 1
        elif (not end_only) and line == 'else' and open_clauses == 0:
            return ['else', i]
        elif (not end_only) and allow_catch and \
             line.startswith('catch ') and open_clauses == 0:
            return ['catch', i]
        else:
            for opener in openers:
                if line.startswith(opener) or line == 'try':
                    open_clauses += 1
                    break
        i += 1
    throw_exception('NoEndStatement',
                    'Corresponding end statement does not exist.')
Exemplo n.º 2
0
def build_type_table(tree):
    """
    Creates a dictionary where all the nodes of a tree
    are directly accessible by name.
    
    e.g.
       A
      / \
     B   C
          \
           D
    will become:
    {
        'A': (tree A containing B,C),
        'B': (tree B),
        'C': (tree C containing D),
        'D': (tree D)
    }
    """
    entries = {}
    for name in tree.names:
        entries[name] = tree
    for subclass in tree.subclasses:
        child_entries = build_type_table(subclass)
        for key, val in child_entries.items():
            if key in entries:
                throw_exception('CannotBuildTypeTable',
                                'Duplicate node name {0}'.format(key))
            else:
                entries[key] = val
    return entries
Exemplo n.º 3
0
def validate_string_index(string, i):
    length = len(string)
    # Indices can be negative, with -1 signifying the last char
    if i < -length or i >= length:
        exception.throw_exception(
            'StringIndexOutOfBounds',
            'Index {0} is out of bounds for {1}'.format(i, string))
Exemplo n.º 4
0
def split_args(args):
    """
    Given a comma-separated argument list, returns the individual
    arguments as a list.
    e.g. '(x+1)*8, y, 25' -> ['(x+1)*8', ' y', ' 25']
    """
    i = 0
    buffer = ''
    results = []
    while i < len(args):
        char = args[i]
        if char == '(':
            offset = find_matching(args[i + 1:])
            if offset == -1:
                throw_exception('UnmatchedOpeningParenthesis', args)
            buffer += args[i:i + offset]
            i += offset
        elif char == '[':
            offset = find_matching(args[i + 1:], '[', ']')
            if offset == -1:
                throw_exception('UnmatchedOpeningSquareBracket', args)
            buffer += args[i:i + offset]
            i += offset
        elif char == ',':
            results.append(buffer)
            buffer = ''
            i += 1
        else:
            buffer += char
            i += 1
    if len(buffer) > 0:
        results.append(buffer)
    return results
Exemplo n.º 5
0
 def execute(self, arg_values, env):
     """
     Executes this function's code, given argument values
     and an environment. Returns the result of the function.
     """
     if len(self.args) != len(arg_values):
         throw_exception('ArgValueException',
                         'Number of arguments does not match function definition')
     if self.supplied_env is not None:
         env = self.supplied_env
     env.new_frame()
     env.merge_latest(self.defined_funcs)
     i = 0
     # Put function arguments into environment frame:
     while i < len(self.args):
         current_arg = self.args[i]
         current_val = arg_values[i]
         env.assign(current_arg, current_val)
         i += 1
     result = execute_lines(self.lines, env)
     # Remove the stack frame created by this function:
     env.pop()
     # Remove the last this pointer:
     if self.is_method:
         env.pop_this()
     if env.value_is_a(result, self.return_type):
         return result
     else:
         throw_exception('IncorrectReturnType', '{0} is not of type {1}'.format(result, self.return_type))
Exemplo n.º 6
0
def eval_parentheses(expr, env):
    """
    Recursively evaluates expressions enclosed in parentheses,
    which change the order-of-operations for the expression.
    """
    ast = AST(expr)
    tokens = ast.parse()
    tokens = call_functions(tokens, env)
    if tokens.__class__ is Trigger:
        return tokens
    # For function values, do not transform the value to a string:
    if len(tokens) == 1 and (str(tokens[0]).startswith('<function') or \
        type(tokens[0]) is list or type(tokens[0]) is dict):
        return tokens[0]
    expr = ''.join(str(token) for token in tokens)
    paren_open = expr.find('(')
    if paren_open == -1:
        return evaluate_expression(expr, env)
    else:
        paren_close = find_matching(expr[paren_open + 1:]) + paren_open
        if paren_close == -1:
            throw_exception('UnmatchedOpeningParenthesis', expr)
        left = expr[0:paren_open]
        center = expr[paren_open + 1:paren_close]
        right = expr[paren_close + 1:]
        return eval_parentheses(left + str(eval_parentheses(center, env)) + right, env)
Exemplo n.º 7
0
def split_args(args):
    """
    Given a comma-separated argument list, returns the individual
    arguments as a list.
    e.g. '(x+1)*8, y, 25' -> ['(x+1)*8', ' y', ' 25']
    """
    i = 0
    buffer = ''
    results = []
    while i < len(args):
        char = args[i]
        if char == '(':
            offset = find_matching(args[i + 1:])
            if offset == -1:
                throw_exception('UnmatchedOpeningParenthesis', args)
            buffer += args[i : i+offset]
            i += offset
        elif char == '[':
            offset = find_matching(args[i + 1:], '[', ']')
            if offset == -1:
                throw_exception('UnmatchedOpeningSquareBracket', args)
            buffer += args[i : i+offset]
            i += offset
        elif char == ',':
            results.append(buffer)
            buffer = ''
            i += 1
        else:
            buffer += char
            i += 1
    if len(buffer) > 0:
        results.append(buffer)
    return results
Exemplo n.º 8
0
def find_next_end_else(lines, start, end_only=False, allow_catch=False):
    """
    Returns the corresponding end-statement or else-statement
    or catch-statement
    for the given clause-opener (which can be an if-statement,
    while-statement, etc.)
    Skips over end and else statements that are part of nested clauses.
    If end_only is set to True, all else statements are passed over.
    """
    i = start
    open_clauses = 0
    openers = ['if ', 'while ', 'for ', 'repeat ', 'switch ', 'sub ']
    while i < len(lines):
        line = lines[i]
        if line == 'end':
            if open_clauses == 0:
                return ['end', i]
            else:
                open_clauses -= 1
        elif (not end_only) and line == 'else' and open_clauses == 0:
            return ['else', i]
        elif (not end_only) and allow_catch and \
             line.startswith('catch ') and open_clauses == 0:
            return ['catch', i]
        else:
            for opener in openers:
                if line.startswith(opener) or line == 'try':
                    open_clauses += 1
                    break
        i += 1
    throw_exception('NoEndStatement', 'Corresponding end statement does not exist.')
Exemplo n.º 9
0
def perform_import(library, env):
    library = library.strip()
    obj = None
    if ' into ' in library:
        pieces = library.split()
        library = pieces[0]
        obj = pieces[2]
        env.new_frame()
    if library == 'math':
        import_math(env)
    elif library == 'functional':
        import_functional(env)
    elif library == 'dataStructures':
        import_data_structures(env)
    elif library == 'debugging':
        import_debugging(env)
    elif library == 'prettyPrint':
        import_pretty_print(env)
    elif library == 'random':
        import_random(env)
    elif library == 'file':
        import_file(env)
    elif library.startswith('"') and library.endswith('"'):
        # Import a program from the file system
        capacita.execute_file(library[1:-1], env)
    else:
        throw_exception('NoExistingLibrary',
                        'No library named {0}'.format(library))
    if obj is not None:
        frame = env.pop()
        env.assign(obj, frame)
Exemplo n.º 10
0
 def char_num(string):
     if len(string) == 0:
         throw_exception(
             'StringEmpty',
             'Cannot convert first character to integer when string is empty'
         )
     return ord(string[0])
Exemplo n.º 11
0
def get_type(value):
    kind = type(value)
    if kind is int:
        return 'Int'
    elif kind is float:
        return 'Double'
    elif kind is bool:
        return 'Boolean'
    elif kind is str:
        if value.startswith('#'):
            return 'Tag'
        else:
            return 'String'
    elif kind is list:
        return 'List'
    if value.__class__ is Ratio:
        return 'Ratio'
    elif value.__class__ is Table:
        return 'Table'
    elif value is None:
        return 'Null'
    elif kind is dict:
        # This is a user-defined object
        if '$type' in value:
            if value['$type'].startswith('"'):
                return value['$type'][1:-1]
            else:
                return value['$type']
        else:
            return 'Instance'
    elif str(value).startswith('<function'):
        return 'Function'
    elif value.__class__ is Trigger:
        return 'Trigger'
    throw_exception('UnknownType', str(value) + ' has an unknown type.')
Exemplo n.º 12
0
 def get(self, key):
     if hashable(key) and key in self.hashable_pairs:
         return self.hashable_pairs[key]
     else:
         for k, v in self.unhashable_pairs:
             if k == key:
                 return v
     throw_exception('KeyNotFound', 'Table has no key ' + str(key))
Exemplo n.º 13
0
 def modify_existing_unhashable(self, key, value):
     for i in xrange(len(self.unhashable_pairs)):
         k, v = self.unhashable_pairs[i]
         if k == key:
             self.unhashable_pairs[i] = (key, value)
             return
     throw_exception('ModifyingKeyNotFound', 'Trying to modify key ' +
                     str(key) + ' that does not exist')
Exemplo n.º 14
0
 def get(self, key):
     if hashable(key) and key in self.hashable_pairs:
         return self.hashable_pairs[key]
     else:
         for k, v in self.unhashable_pairs:
             if k == key:
                 return v
     throw_exception('KeyNotFound', 'Table has no key ' + str(key))
Exemplo n.º 15
0
def type_restrict(val, type_tree, env):
    kind = type_tree.get_default_name()
    if type(val) is list:
        return RestrictedList(val, kind, env)
    else:
        throw_exception(
            'CannotTypeRestrictValue',
            'Value {0} cannot be restricted with type {1}'.format(val, kind)
        )
Exemplo n.º 16
0
 def modify_existing_unhashable(self, key, value):
     for i in xrange(len(self.unhashable_pairs)):
         k, v = self.unhashable_pairs[i]
         if k == key:
             self.unhashable_pairs[i] = (key, value)
             return
     throw_exception(
         'ModifyingKeyNotFound',
         'Trying to modify key ' + str(key) + ' that does not exist')
Exemplo n.º 17
0
 def update(self, var_name, value):
     # TODO : env.update(...) should be removed in favor
     # of transforms such as (age += 1) -> (age = age + 1)
     # Therefore the added functionality of env.assign(...)
     # will apply to these statements.
     if var_name in self.frames[-1]:
         self.frames[-1][var_name] = value
     else:
         throw_exception('UndefinedVariable', var_name + ' is not defined.')
Exemplo n.º 18
0
 def execute(self, arg_values, env=None):
     if not self.check_args_within_bounds(arg_values):
         throw_exception(
             'IncorrectNumberOfArguments',
             'Wrong number of arguments passed to built-in function\n' \
             'Expected between {0} and {1}, but received {2}'.format(
                 self.min_args, self.max_args, len(arg_values)
             )
         )
     return self.action(*arg_values)
Exemplo n.º 19
0
def set_digit(n, index, radix, new_digit):
    check_radix(radix)
    sign = -1 if n < 0 else 1
    n = abs(n)
    if (not is_int(new_digit)) or new_digit < 0 or new_digit >= radix:
        throw_exception('UnsupportedDigit', 'Digit must be between 0 and {0} inclusive'.format(radix - 1))
    prev_digit = get_digit(n, index, radix)
    scale = radix ** index
    prev_value = prev_digit * scale
    n -= prev_value
    n += new_digit * scale
    return sign * n
Exemplo n.º 20
0
 def get(self, var_name):
     last_this = self.last_this()
     if var_name in last_this:
         return last_this[var_name]
     i = -1
     while i >= -len(self.frames):
         if var_name in self.frames[i]:
             value = self.frames[i][var_name]
             if type(value) is tuple:
                 return value[1]
             return value
         else:
             i -= 1
     throw_exception('UndefinedVariable', var_name + ' is not defined.')
Exemplo n.º 21
0
 def insert_skips(lines):
     """
     Insert :skiptoelse statements into try-catch blocks
     """
     i = 0
     while i < len(lines):
         line = lines[i]
         if line == 'try':
             kind, j = find_next_end_else(lines, i + 1, False, True)
             if kind != 'catch':
                 throw_exception('CannotFindCatch', 'Line found that was not catch statement: ' + str(lines[j]))
             catch_statement = lines[j]
             lines[j : j+1] = [':skiptoelse', catch_statement]
         i += 1
     return lines
Exemplo n.º 22
0
    def partial(f, fixed_args):
        num_original_args = f.get_num_args()
        num_fixed_args = len(fixed_args)
        num_args_needed = num_original_args - num_fixed_args
        if num_args_needed < 0:
            throw_exception(
                'TooManyFixedArguments',
                'Function {0} requires {1} arguments, but passing in {2}'.
                format(f.name, num_original_args, num_fixed_args))
        arg_names = ['x' + str(i) for i in xrange(num_args_needed)]

        def partially_applied_f(*args):
            return f.execute(fixed_args + list(args), env)

        return builtin_function.BuiltinFunction('fPrime', arg_names,
                                                partially_applied_f)
Exemplo n.º 23
0
 def assign(self, var_name, value):
     self.last_assigned = value
     # Check for a type restriction
     match_obj = re.match(r'([A-Za-z_][A-Za-z_0-9]*) (\$?[A-Za-z_][A-Za-z_0-9]*)', var_name)
     if match_obj:
         kind = match_obj.group(1)
         if not self.value_is_a(value, kind):
             throw_exception('MismatchedType', str(value) + ' is not of type ' + kind +
                             '\nType tree is: ' + str(self.all_types))
         var = match_obj.group(2)
         frame_or_this = self.get_frame_or_this(var)
         if var in frame_or_this:
             type_tuple = frame_or_this[var]
             if type(type_tuple) is tuple:
                 existing_kind = type_tuple[0]
                 if kind != existing_kind:
                     throw_exception('AlreadyRestrictedType', var + ' already has type ' + existing_kind)
             else:
                 throw_exception('AlreadyAnyType',
                     var + ' already has type Any and cannot be further restricted.')
         frame_or_this[var] = (kind, value)
     else:
         # Is this an assignment to an index?
         match_obj = re.match(r'(\$?[A-Za-z_][A-Za-z_0-9]*)\[(.+)\]', var_name)
         if match_obj:
             indexable = match_obj.group(1)
             index = eval_parentheses(match_obj.group(2), self)
             frame_or_this = self.get_frame_or_this(indexable)
             container = frame_or_this[indexable]
             if type(container) is tuple:
                 # Modify the iterable contained within the tuple
                 iterable = container[1]
                 iterable[index] = value
             else:
                 # This iterable has no type restriction
                 frame_or_this[indexable][index] = value
         else:
             # Is this an attribute of an object?
             match_obj = re.match(r'(\$?[A-Za-z_][A-Za-z_0-9]*)\.(\$?[A-Za-z_][A-Za-z_0-9]*)', var_name)
             if match_obj:
                 obj = match_obj.group(1)
                 attr = match_obj.group(2)
                 frame_or_this = self.get_frame_or_this(obj)
                 frame_or_this[obj][attr] = value
             else:
                 frame_or_this = self.get_frame_or_this(var_name)
                 if var_name in frame_or_this and type(frame_or_this[var_name]) is tuple:
                     # There is an existing type restriction
                     kind = frame_or_this[var_name][0]
                     if not self.value_is_a(value, kind):
                         throw_exception('MismatchedType', str(value) + ' is not of type ' + kind +
                                         '\nType tree is: ' + str(self.all_types))
                     frame_or_this[var_name] = (kind, value)
                 else:
                     # There is no type restriction given
                     frame_or_this[var_name] = value
Exemplo n.º 24
0
 def insert_skips(line_mgr):
     """
     Insert :skiptoelse statements into try-catch blocks
     """
     i = 0
     while i < len(line_mgr):
         line = line_mgr[i]
         if line == 'try':
             kind, j = find_next_end_else(line_mgr, i + 1, False, True)
             if kind != 'catch':
                 throw_exception(
                     'CannotFindCatch',
                     'Line found that was not catch statement: ' +
                     str(line_mgr[j]))
             catch_statement = line_mgr[j]
             line_mgr[j:j + 1] = [':skiptoelse', catch_statement]
         i += 1
Exemplo n.º 25
0
 def parse_elem(self, expr):
     """
     Splits an expression based on its operators.
     e.g. '2 + 3*4' -> ['2', '+', '3', '*', '4']
     """
     buffer = ''
     tokens = []
     expr_length = len(expr)
     i = 0
     in_quotes = False
     while i < expr_length:
         next_char = expr[i]
         if next_char == '(':
             paren_close = find_matching(expr[i + 1:])
             if paren_close == -1:
                 throw_exception('UnmatchedOpeningParenthesis', expr)
             buffer += '(' + expr[i+1 : i+paren_close]
             i += paren_close
         else:
             op_detected = False
             if not in_quotes:
                 for op in self.ordered_ops:
                     op_length = len(op)
                     if not(op == '.' and is_digit(expr[i+op_length : i+op_length+1])) and \
                        expr[i : i+op_length] == op:
                         buffer_contents = buffer.strip()
                         if len(buffer_contents) > 0:
                             tokens.append(buffer_contents)
                         tokens.append(op)
                         buffer = ''
                         i += op_length
                         op_detected = True
                         break
             if not op_detected:
                 if next_char == '"':
                     if in_quotes:
                         in_quotes = False
                     else:
                         in_quotes = True
                 buffer += next_char
                 i += 1
     buffer_contents = buffer.strip()
     if len(buffer_contents) > 0:
         tokens.append(buffer_contents)
     return self.merge_negatives(tokens)
Exemplo n.º 26
0
def test_all(path, has_delay=True, use_repl=False):
    files = [f for f in os.listdir(path) if f.endswith('.cap')]
    print '===== Starting tests...'
    is_first_file = True
    for file in files:
        if has_delay and not is_first_file:
            time.sleep(3)
        is_first_file = False
        full_path = path + '/' + file
        print '===== Testing ' + full_path
        contents = fileio.file_to_str(full_path)
        try:
            test_program(contents, use_repl)
        except NameError as ex:
            exception.throw_exception('UnexpectedNameError', str(ex))
        except BaseException as ex:
            print '===== Exception: ' + str(ex.__class__)
    print '===== Done.'
Exemplo n.º 27
0
 def parse_elem(self, expr, prev_elem):
     """
     Splits an expression based on its operators.
     e.g. '2 + 3*4' -> ['2', '+', '3', '*', '4']
     """
     buffer = ''
     tokens = []
     expr_length = len(expr)
     i = 0
     in_quotes = False
     while i < expr_length:
         next_char = expr[i]
         if next_char == '(':
             paren_close = find_matching(expr[i + 1:])
             if paren_close == -1:
                 throw_exception('UnmatchedOpeningParenthesis', expr)
             buffer += '(' + expr[i + 1:i + paren_close]
             i += paren_close
         else:
             op_detected = False
             if (not in_quotes) and next_char in self.starters:
                 for op in self.ordered_ops:
                     op_length = len(op)
                     if expr[i : i+op_length] == op and \
                        not(op == '.' and is_digit(expr[i+1 : i+2])):
                         buffer_contents = buffer.strip()
                         if len(buffer_contents) > 0:
                             tokens.append(buffer_contents)
                         tokens.append(op)
                         buffer = ''
                         i += op_length
                         op_detected = True
                         break
             if not op_detected:
                 if next_char == '"':
                     in_quotes = not in_quotes
                 buffer += next_char
                 i += 1
     buffer_contents = buffer.strip()
     if len(buffer_contents) > 0:
         tokens.append(buffer_contents)
     return self.merge_negatives(self.merge_exponent_notation(tokens),
                                 prev_elem)
Exemplo n.º 28
0
def dot_operator(obj, name, env):
    if type(obj) is str and re.match(r'\$?[A-Za-z_][A-Za-z_0-9]*', obj):
        obj = env.get(obj)
    obj = execution.convert_value(obj, env)
    method = None
    if type_restrict.is_considered_list(obj):
        method = list_methods(obj, name, env)
    elif obj.__class__ is strtools.CapString:
        method = str_methods(obj, name, env)
    elif type(obj) is dict:
        method = obj_methods(obj, name, env)
    elif type(obj) in [int, long, float]:
        method = number_methods(obj, name, env)
    elif obj.__class__ is Table:
        method = table_methods(obj, name, env)
    elif obj.__class__ is type_tree.TypeTree:
        method = type_tree_methods(obj, name, env)
    if method is None:
        throw_exception('NoSuchAttribute', str(obj) + ' object has no attribute ' + name)
    else:
        return method
Exemplo n.º 29
0
def merge_trees(tree, other):
    """
    Combines two type trees into a single tree.
    The new tree will contain all the children of both
    original trees. Children that are shared will be
    recursively merged.
    
    e.g.
    Take the two trees:
       A        A
      / \      / \
     B   C    D   B
     |           / \
     G          E   F
    The result should be:
         A
        /|\
       B C D
      /|\
     G E F
    """
    if tree.names != other.names:
        throw_exception(
            'CannotMergeTrees', 'Root value {0} is not the same as {1}'.format(
                tree.names, other.names))
    combined_subclasses = {}
    for subclass in tree.subclasses:
        combined_subclasses[subclass.names] = subclass
    for subclass in other.subclasses:
        if subclass.names in combined_subclasses:
            old_tree = combined_subclasses[subclass.names]
            new_tree = merge_trees(old_tree, subclass)
            combined_subclasses[subclass.names] = new_tree
        else:
            combined_subclasses[subclass.names] = subclass
    result = TypeTree(*tree.names)
    for val in combined_subclasses.values():
        result.add_subclass(val)
    return result
Exemplo n.º 30
0
def dot_operator(obj, name, env):
    if type(obj) is str and re.match(r'\$?[A-Za-z_][A-Za-z_0-9]*', obj):
        obj = env.get(obj)
    obj = convert_value(obj, env)
    if type(obj) is list:
        if name == 'length' or name == 'size':
            length = len(obj)
            return BuiltinFunction('constant', [], lambda: length)
        elif name == 'pop':
            last_elem = obj.pop()
            return BuiltinFunction('constant', [], lambda: last_elem)
        elif name == 'push':
            def func_push(new_elem):
                obj.append(new_elem)
                return obj
            return BuiltinFunction('list', ['new_elem'], func_push)
    elif type(obj) is dict:
        # This is a method call
        if obj[name].__class__ is BuiltinFunction:
            # Built in functions don't need a this pointer
            return obj[name]
        else:
            env.new_this(obj)
            method = obj[name]
            method.activate_method()
            return method
    elif type(obj) in [int, float]:
        if name == 'next':
            return BuiltinFunction('constant', [], lambda: obj + 1)
        elif name == 'previous':
            return BuiltinFunction('constant', [], lambda: obj - 1)
    elif obj.__class__ is Table:
        if name == 'length' or name == 'size':
            return BuiltinFunction('constant', [], lambda: len(obj))
        elif name == 'keys':
            return BuiltinFunction('constant', [], lambda: obj.keys())
        elif name == 'hasKey':
            return BuiltinFunction('hasKey', ['key'], lambda key: obj.has_key(key))
    throw_exception('NoSuchAttribute', str(obj) + ' object has no attribute ' + name)
Exemplo n.º 31
0
    def split_list_elems(self):
        """
        Splits apart list and table literals into brackets, commas, and elements.
        e.g. '[1, 2+3, 4*5]' -> ['[', '1', ',', '2+3', ',', '4*5', ']']
        '{1, 2 | 3, 4}' -> ['{', '1', ',', '2', '|', '3', ',', '4', '}']
        """
        def append_if_not_empty(lst, elem):
            if len(elem) > 0:
                lst.append(elem)

        results = []
        buffer = ''
        delimiters = '[],{}|'
        i = 0
        while i < len(self.expr):
            char = self.expr[i]
            if char in delimiters:
                append_if_not_empty(results, buffer)
                results.append(char)
                buffer = ''
            elif char == '(':
                j = find_matching(self.expr[i + 1:])
                if j == -1:
                    throw_exception('UnmatchedOpeningParenthesis', self.expr)
                buffer += self.expr[i:i + j]
                i += j
                continue
            elif char == '"':
                j = find_matching_quote(self.expr[i + 1:])
                if j == -1:
                    throw_exception('UnmatchedQuote', self.expr)
                buffer += self.expr[i:i + j + 1] + '"'
                i += j + 1
            else:
                buffer += char
            i += 1
        append_if_not_empty(results, buffer)
        return results
Exemplo n.º 32
0
def get_type(value):
    kind = type(value)
    if kind is int or kind is long:
        return 'Int'
    elif kind is float:
        return 'Double'
    elif kind is bool:
        return 'Boolean'
    elif kind is str:
        if value.startswith('#'):
            return 'Tag'
        throw_exception('UnknownValueType',
                        'The type of {0} cannot be determined'.format(value))
    elif kind is list:
        return 'List'
    val_class = value.__class__
    if val_class is strtools.CapString:
        return 'String'
    if val_class is Ratio:
        return 'Ratio'
    elif val_class is Table:
        return 'Table'
    elif value is None:
        return 'Null'
    elif kind is dict:
        # This is a user-defined object
        if '$type' in value:
            if value['$type'].__class__ is strtools.CapString:
                return value['$type'].contents
            else:
                return value['$type']
        else:
            return 'Instance'
    elif function.is_function(value):
        return 'Function'
    elif val_class is Trigger:
        return 'Trigger'
    throw_exception('UnknownType', str(value) + ' has an unknown type.')
Exemplo n.º 33
0
 def split_list_elems(self):
     """
     Splits apart list and table literals into brackets, commas, and elements.
     e.g. '[1, 2+3, 4*5]' -> ['[', '1', ',', '2+3', ',', '4*5', ']']
     '{1, 2 | 3, 4}' -> ['{', '1', ',', '2', '|', '3', ',', '4', '}']
     """
     def append_if_not_empty(lst, elem):
         if len(elem) > 0:
             lst.append(elem)
     results = []
     buffer = ''
     delimiters = ['[', ']', ',', '{', '}', '|']
     i = 0
     while i < len(self.expr):
         char = self.expr[i]
         if char in delimiters:
             append_if_not_empty(results, buffer)
             results.append(char)
             buffer = ''
         elif char == '(':
             j = find_matching(self.expr[i + 1:])
             if j == -1:
                 throw_exception('UnmatchedOpeningParenthesis', self.expr)
             buffer += self.expr[i : i+j]
             i += j
             continue
         elif char == '"':
             j = find_matching_quote(self.expr[i + 1:])
             if j == -1:
                 throw_exception('UnmatchedQuote', self.expr)
             buffer += self.expr[i : i+j+1] + '"'
             i += j + 1
         else:
             buffer += char
         i += 1
     append_if_not_empty(results, buffer)
     return results
Exemplo n.º 34
0
 def feed(*args):
     if len(args) == 0:
         throw_exception('NotEnoughArguments',
                         'Function requires at least 1 argument')
     result = args[0]
     for elem in args[1:]:
         if function.is_function(elem):
             result = elem.execute([result], env)
         elif type(elem) is list:
             if len(elem) == 0 or not function.is_function(elem[0]):
                 throw_exception(
                     'InvalidArgument',
                     'List must contain a function as first element')
             f = elem[0]
             rest = list(elem[1:])
             result = f.execute(rest + [result], env)
         else:
             throw_exception('InvalidArgument',
                             'Argument must be a list or function')
     return result
Exemplo n.º 35
0
def evaluate_operators(tokens, indices, env):
    """
    Evaluates an expression based on a list of tokens,
    indices where the operators are located, and
    environment env.
    """
    brackets = ['[', ']', '{', '}']
    transform_string_literals(tokens)
    for idx in indices:
        # TODO : checking bounds should no longer be
        # necessary after fixing the xrange issue.
        # (passing in an xrange as operator indices)
        if idx >= len(tokens):
            break
        op = tokens[idx]
        is_dot = op == '.'
        if idx > 0:
            left = convert_value(tokens[idx - 1], env)
        else:
            left = None
        if idx + 1 >= len(tokens):
            # This index is not valid:
            break
        if not is_dot:
            right = convert_value(tokens[idx + 1], env)
        else:
            right = tokens[idx + 1]
        if left in brackets or right in brackets:
            break
        left, right = promote_values(left, right)
        # TODO : support user-defined methods such as $eq, $notEq when evaluating operators
        if is_dot:
            tokens[idx - 1:idx +
                   2] = [environment.get_typed_value(left[right])]
        elif op != ' of ' and operator_overload.ready_for_overload(
                op, left, right):
            # Perform operator overloading
            result = operator_overload.operator_overload(
                op, left, right, idx, tokens, env)
            return_val = result['return_val']
            if result['is_unary']:
                tokens[idx:idx + 2] = [return_val]
            else:
                tokens[idx - 1:idx + 2] = [return_val]
        elif op == '+':
            tokens[idx - 1:idx + 2] = [left + right]
        elif op == '-':
            tokens[idx - 1:idx + 2] = [left - right]
        elif op == '~':
            tokens[idx:idx + 2] = [-right]
        elif op == '*':
            tokens[idx - 1:idx + 2] = [left * right]
        elif op == '/':
            tokens[idx - 1:idx + 2] = [divide(left, right)]
        elif op == '%':
            tokens[idx - 1:idx + 2] = [left % right]
        elif op == '==':
            tokens[idx - 1:idx + 2] = [left == right]
        elif op == '!=':
            tokens[idx - 1:idx + 2] = [left != right]
        elif op == '>=':
            tokens[idx - 1:idx + 2] = [left >= right]
        elif op == '<=':
            tokens[idx - 1:idx + 2] = [left <= right]
        elif op == '>':
            tokens[idx - 1:idx + 2] = [left > right]
        elif op == '<':
            tokens[idx - 1:idx + 2] = [left < right]
        elif op == ' and ':
            tokens[idx - 1:idx + 2] = [left and right]
        elif op == ' or ':
            tokens[idx - 1:idx + 2] = [left or right]
        elif op == 'not ':
            tokens[idx:idx + 2] = [not right]
        elif op == '^':
            tokens[idx - 1:idx + 2] = [left**right]
        elif op == ':':
            tokens[idx - 1:idx + 2] = [Ratio(left, right)]
        elif op == ' of ':
            tokens[idx - 1:idx +
                   2] = [type_restrict.type_restrict(left, right, env)]
        elif op == ' xor ':
            tokens[idx - 1:idx + 2] = [(left and not right)
                                       or ((not left) and right)]
    stage = 0
    while len(tokens) != 1:
        if stage == 0:
            tokens = evaluate_list(tokens, env)
            stage = 1
        elif stage == 1:
            tokens = index_lists(tokens, env)
            stage = 2
        elif stage == 2:
            input_tokens = tokens
            # TODO : avoid having to pass in an xrange of possible operator indices
            tokens = evaluate_operators(tokens, xrange(len(tokens) - 1), env)
            # If the value returned is no longer the outer list, it means
            # an inner list is the result:
            if tokens is not input_tokens:
                return tokens
            stage = 3
        elif stage == 3:
            throw_exception(
                'ExprEval',
                str(tokens) + ' cannot be converted into a single value')
    # Return the resulting (single) value without the outer list.
    return convert_value(tokens[0], env)
Exemplo n.º 36
0
def prepare_control_flow(line_mgr):
    """
    Converts if-statements, while-statements, for-statements, etc.
    into :cond and jump directives
    (:j for unconditional jump,
     :jt for jump true, and :jf for jump false).

    e.g.
    x = 100
    if x > 0
        // do something
    else
        // do other thing
    end

    0: x = 100
    1: :cond x > 0
    2: :jf 5
    3: // do something
    4: :j 6
    5: // do other thing
    6: 
    """
    def replace_labels(line_mgr):
        """
        Replaces labels in jump directives with the actual
        addresses being jumped to.
        """
        label_table = {}
        i = 0
        for line in line_mgr.get_lines():
            if line.startswith(':label'):
                label_table[line[6:]] = i
                # Erase the label at this line:
                line_mgr[i] = ''
            i += 1
        i = 0
        jumps = [':j ', ':jt ', ':jf ']
        for line in line_mgr.get_lines():
            for jump in jumps:
                if line.startswith(jump + 'label'):
                    label_num = line[len(jump) + 5:]
                    if label_num in label_table:
                        line_mgr[i] = jump + str(label_table[label_num])
                        break
            i += 1

    def prepare_else_ifs(line_mgr):
        """
        Splits else-if statements into two separate lines:
        else followed by an if.
        Adds additional end statements for padding.
        This allows else-if statements to be evaluated using
        only basic if-else logic. For example, the following
        structures are equivalent:
        
        x = 2
        if x == 1
            A
        else if x == 2
            B
        else
            C
        end
        
        x = 2
        if x == 1
            A
        else
            if x == 2
                B
            else
                C
            end
        end
        """
        i = 0
        while i < len(line_mgr):
            line = line_mgr[i]
            if line.startswith('if '):
                # Find the end statement:
                _, end = find_next_end_else(line_mgr, i + 1, True)
                j = i + 1
                # Keep track of how many else-ifs were split:
                splits = 0
                while j < end:
                    other_line = line_mgr[j]
                    if other_line.startswith('else if '):
                        line_mgr[j:j + 1] = ['else', other_line[5:]]
                        end += 1
                        splits += 1
                    j += 1
                # Add extra end statements:
                line_mgr[end:end + 1] = ['end' for _ in xrange(splits + 1)]
            i += 1

    def prepare_breaks_continues(line):
        """
        Replace 'break' with 'break 1' and 'continue' with
        'continue 1'.
        Thus, all breaks and continues will be supplied
        with a depth parameter.
        """
        if line == 'break':
            return 'break 1'
        elif line == 'continue':
            return 'continue 1'
        else:
            return line

    def replace_for(line_mgr):
        """
        Replaces for loops with for directives,
        eventually translated to the equivalent of a while loop.
        
        for i = 0; i < 10; i++
            print i
        end
        
        i = 0
        while i < 10
            print i
            i++
        end
        """
        i = 0
        while i < len(line_mgr):
            line = line_mgr[i]
            if line.startswith('for '):
                initialization = line[4:]
                condition = line_mgr[i + 1]
                increment = line_mgr[i + 2]
                _, j = find_next_end_else(line_mgr, i + 1, True)
                line_mgr[j:j + 1] = [increment, 'end']
                line_mgr[i:i +
                         3] = [initialization, ':for ' + condition, increment]
            i += 1

    def replace_for_each(line_mgr):
        """
        Replaces for-each loops with while loops.
        
        for each x of xs
            print x
        end
        
        $v0 = 0
        while $v0 < xs.length()
            x = xs[$v0]
            print x
            $v0++
        end
        """
        i = 0
        index_var_num = 0
        while i < len(line_mgr):
            line = line_mgr[i]
            match_obj = re.match(r'for each ([A-Za-z_][A-Za-z_0-9]*) of (.*)',
                                 line)
            if match_obj:
                elem_var = match_obj.group(1)
                iterable = match_obj.group(2)
                index_var = '$i' + str(index_var_num)
                initialization = index_var + '=0'
                condition = index_var + '<' + iterable + '.length()'
                increment = index_var + '+=1'
                assignment = elem_var + '=' + iterable + '[' + index_var + ']'
                _, j = find_next_end_else(line_mgr, i + 1, True)
                line_mgr[j:j + 1] = [increment, 'end']
                # Temporarily place the increment statement after the :for directive,
                # so it can be used when processing continue statements.
                line_mgr[i:i + 1] = [
                    initialization, ':for ' + condition, increment, assignment
                ]
                index_var_num += 1
            i += 1

    def insert_skips(line_mgr):
        """
        Insert :skiptoelse statements into try-catch blocks
        """
        i = 0
        while i < len(line_mgr):
            line = line_mgr[i]
            if line == 'try':
                kind, j = find_next_end_else(line_mgr, i + 1, False, True)
                if kind != 'catch':
                    throw_exception(
                        'CannotFindCatch',
                        'Line found that was not catch statement: ' +
                        str(line_mgr[j]))
                catch_statement = line_mgr[j]
                line_mgr[j:j + 1] = [':skiptoelse', catch_statement]
            i += 1

    def replace_when_continue(line_mgr):
        """
        Transforms a when statement containing a continue in its clause
        into an if statement.

        when CONDITION
            continue 2

        if CONDITION
            continue 2
        end

        This is so that continue statements can be replaced by an
        increment statement followed by a continue, which is needed
        in for loops and for-each loops.
        """
        i = 0
        while i + 1 < len(line_mgr):
            current_line = line_mgr[i]
            next_line = line_mgr[i + 1]
            if current_line.startswith('when ') and next_line.startswith(
                    'continue '):
                line_mgr[i:i +
                         2] = ['if ' + current_line[5:], next_line, 'end']
            i += 1

    prepare_else_ifs(line_mgr)
    line_mgr.for_each_line(prepare_breaks_continues)
    replace_when_continue(line_mgr)
    replace_for_each(line_mgr)
    replace_for(line_mgr)
    insert_skips(line_mgr)
    i = 0
    label_counter = 0
    while i < len(line_mgr):
        line = line_mgr[i]
        if line.startswith('when '):
            line_mgr[i:i + 1] = [':cond ' + line[5:], ':jf ' + str(i + 3)]
        elif line.startswith('if '):
            line_mgr[i:i + 1] = [
                ':cond ' + line[3:], ':jf label' + str(label_counter)
            ]
            # Find the next else or end statement:
            kind, j = find_next_end_else(line_mgr, i + 2)
            if kind == 'end':
                line_mgr[j] = ':label' + str(label_counter)
            else:
                # kind must be an else statement.
                # replace the else statement with a jump:
                else_label = ':label' + str(label_counter)
                label_counter += 1
                line_mgr[j:j +
                         1] = [':j label' + str(label_counter), else_label]
                kind, end = find_next_end_else(line_mgr, j + 1)
                if kind == 'else':
                    throw_exception(
                        'MultipleElseStatement',
                        'if statements cannot have multiple else clauses'
                        ' (aside from else-if statements).')
                line_mgr[end] = ':label' + str(label_counter)
            label_counter += 1
        elif is_loop(line):
            if line.startswith(':for '):
                # Remove the increment statement, and save it for later use
                increment_statement = line_mgr.pop(i + 1)
                condition_start = 5
            else:
                increment_statement = None
                condition_start = 6
            # label_leave is used to exit the loop:
            label_leave = str(label_counter)
            label_counter += 1
            # label_loop is used to repeat the loop:
            label_loop = str(label_counter)
            label_counter += 1
            # Replace the existing while statement or :for directive with a :cond directive
            line_mgr[i:i + 1] = [
                ':cond ' + line[condition_start:], ':jf label' + label_leave,
                ':label' + label_loop
            ]
            kind, j = find_next_end_else(line_mgr, i + 3)
            if kind == 'end':
                line_mgr[j:j + 1] = [
                    ':cond ' + line[condition_start:],
                    ':jt label' + label_loop, ':label' + label_leave
                ]
                k = i + 3
                depth = 1
                # stack for keeping track of end statements:
                stack = []
                # Handle 'continue' and 'break' statements:
                while k < j:
                    current = line_mgr[k]
                    if is_loop(current):
                        _, end = find_next_end_else(line_mgr, k + 1, True)
                        depth += 1
                        stack.append(end)
                    elif len(
                            stack) > 0 and current == 'end' and stack[-1] == k:
                        # This is the end to a nested while loop:
                        stack.pop()
                        depth -= 1
                    elif current == 'break ' + str(depth):
                        line_mgr[k] = ':j label' + label_leave
                    elif current == 'continue ' + str(depth):
                        jump_to_loop_start = ':j label' + label_loop
                        if increment_statement is not None:
                            line_mgr[k:k + 1] = [
                                increment_statement, jump_to_loop_start
                            ]
                        else:
                            line_mgr[k] = jump_to_loop_start
                    k += 1
            else:
                throw_exception('InvalidElseStatement',
                                'While loops cannot have else statements.')
        i += 1
    replace_labels(line_mgr)
Exemplo n.º 37
0
def enforce_type(val, kind, env, msg=''):
    if not env.value_is_a(val, kind):
        throw_exception(
            'IncorrectTypeGiven',
            '{0} is not of type {1} {2}'.format(val, kind, msg)
        )
Exemplo n.º 38
0
def prepare_control_flow(lines):
    """
    Converts if-statements, while-statements, for-statements, etc.
    into :cond and jump directives
    (:j for unconditional jump,
     :jt for jump true, and :jf for jump false).

    e.g.
    x = 100
    if x > 0
        // do something
    else
        // do other thing
    end

    0: x = 100
    1: :cond x > 0
    2: :jf 5
    3: // do something
    4: :j 6
    5: // do other thing
    6: 
    """
    def replace_labels(lines):
        """
        Replaces labels in jump directives with the actual
        addresses being jumped to.
        """
        label_table = {}
        i = 0
        for line in lines:
            if line.startswith(':label'):
                label_table[line[6:]] = i
                # Erase the label at this line:
                lines[i] = ''
            i += 1
        i = 0
        jumps = [':j ', ':jt ', ':jf ']
        for line in lines:
            for jump in jumps:
                if line.startswith(jump + 'label'):
                    label_num = line[len(jump) + 5:]
                    if label_num in label_table:
                        lines[i] = jump + str(label_table[label_num])
                        break
            i += 1
        return lines
    def prepare_else_ifs(lines):
        """
        Splits else-if statements into two separate lines:
        else followed by an if.
        Adds additional end statements for padding.
        This allows else-if statements to be evaluated using
        only basic if-else logic. For example, the following
        structures are equivalent:
        
        x = 2
        if x == 1
            A
        else if x == 2
            B
        else
            C
        end
        
        x = 2
        if x == 1
            A
        else
            if x == 2
                B
            else
                C
            end
        end
        """
        i = 0
        while i < len(lines):
            line = lines[i]
            if line.startswith('if '):
                # Find the end statement:
                _, end = find_next_end_else(lines, i + 1, True)
                j = i + 1
                # Keep track of how many else-ifs were split:
                splits = 0
                while j < end:
                    other_line = lines[j]
                    if other_line.startswith('else if '):
                        lines[j : j+1] = ['else', other_line[5:]]
                        end += 1
                        splits += 1
                    j += 1
                # Add extra end statements:
                lines[end : end+1] = ['end' for _ in xrange(splits + 1)]
            i += 1
        return lines
    def prepare_breaks_continues(lines):
        """
        Replace 'break' with 'break 1' and 'continue' with
        'continue 1'.
        Thus, all breaks and continues will be supplied
        with a depth parameter.
        """
        i = 0
        while i < len(lines):
            line = lines[i]
            if line == 'break':
                lines[i] = 'break 1'
            elif line == 'continue':
                lines[i] = 'continue 1'
            i += 1
        return lines
    def replace_for(lines):
        """
        Replaces for loops with while loops.
        
        for i = 0; i < 10; i++
            print i
        end
        
        i = 0
        while i < 10
            print i
            i++
        end
        """
        i = 0
        while i < len(lines):
            line = lines[i]
            if line.startswith('for '):
                initialization = line[4:]
                condition = lines[i + 1]
                increment = lines[i + 2]
                _, j = find_next_end_else(lines, i + 1, True)
                lines[j : j+1] = [increment, 'end']
                lines[i : i+3] = [initialization, 'while ' + condition]
            i += 1
        return lines
    def replace_for_each(lines):
        """
        Replaces for-each loops with while loops.
        
        for each x of xs
            print x
        end
        
        $v0 = 0
        while $v0 < xs.length()
            x = xs[$v0]
            print x
            $v0++
        end
        """
        i = 0
        index_var_num = 0
        while i < len(lines):
            line = lines[i]
            match_obj = re.match(r'for each ([A-Za-z_][A-Za-z_0-9]*) of (.*)', line)
            if match_obj:
                elem_var = match_obj.group(1)
                iterable = match_obj.group(2)
                index_var = '$i' + str(index_var_num)
                initialization = index_var + '=0'
                condition = index_var + '<' + iterable + '.length()'
                increment = index_var + '+=1'
                assignment = elem_var + '=' + iterable + '[' + index_var + ']'
                _, j = find_next_end_else(lines, i + 1, True)
                lines[j : j+1] = [increment, 'end']
                lines[i : i+1] = [initialization, 'while ' + condition, assignment]
                index_var_num += 1
            i += 1
        return lines
    def insert_skips(lines):
        """
        Insert :skiptoelse statements into try-catch blocks
        """
        i = 0
        while i < len(lines):
            line = lines[i]
            if line == 'try':
                kind, j = find_next_end_else(lines, i + 1, False, True)
                if kind != 'catch':
                    throw_exception('CannotFindCatch', 'Line found that was not catch statement: ' + str(lines[j]))
                catch_statement = lines[j]
                lines[j : j+1] = [':skiptoelse', catch_statement]
            i += 1
        return lines
    lines = prepare_else_ifs(lines)
    lines = prepare_breaks_continues(lines)
    lines = replace_for_each(lines)
    lines = replace_for(lines)
    lines = insert_skips(lines)
    i = 0
    label_counter = 0
    while i < len(lines):
        line = lines[i]
        if line.startswith('when '):
            lines[i : i+1] = [':cond ' + line[5:], ':jf ' + str(i + 3)]
        elif line.startswith('if '):
            lines[i : i+1] = [':cond ' + line[3:], ':jf label' + str(label_counter)]
            # Find the next else or end statement:
            kind, j = find_next_end_else(lines, i + 2)
            if kind == 'end':
                lines[j] = ':label' + str(label_counter)
            else:
                # kind must be an else statement.
                # replace the else statement with a jump:
                else_label = ':label' + str(label_counter)
                label_counter += 1
                lines[j : j+1] = [':j label' + str(label_counter), else_label]
                kind, end = find_next_end_else(lines, j + 1)
                if kind == 'else':
                    throw_exception('MultipleElseStatement',
                                    'if statements cannot have multiple else clauses'
                                    ' (aside from else-if statements).')
                lines[end] = ':label' + str(label_counter)
            label_counter += 1
        elif line.startswith('while '):
            # label_leave is used to exit the loop:
            label_leave = str(label_counter)
            label_counter += 1
            # label_loop is used to repeat the loop:
            label_loop = str(label_counter)
            label_counter += 1
            lines[i : i+1] = [':cond ' + line[6:], ':jf label' + label_leave, ':label' + label_loop]
            kind, j = find_next_end_else(lines, i + 3)
            if kind == 'end':
                lines[j : j+1] = [':cond ' + line[6:], ':jt label' + label_loop, ':label' + label_leave]
                k = i + 3
                depth = 1
                # stack for keeping track of end statements:
                stack = []
                # Handle 'continue' and 'break' statements:
                while k < j:
                    current = lines[k]
                    if current.startswith('while '):
                        _, end = find_next_end_else(lines, k + 1, True)
                        depth += 1
                        stack.append(end)
                    elif len(stack) > 0 and current == 'end' and stack[-1] == k:
                        # This is the end to a nested while loop:
                        stack.pop()
                        depth -= 1
                    elif current == 'break ' + str(depth):
                        lines[k] = ':j label' + label_leave
                    elif current == 'continue ' + str(depth):
                        lines[k] = ':j label' + label_loop
                    k += 1
            else:
                throw_exception('InvalidElseStatement',
                                'While loops cannot have else statements.')
        i += 1
    lines = replace_labels(lines)
    return lines
Exemplo n.º 39
0
 def extract_string(cap_string):
     if cap_string.__class__ is not strtools.CapString:
         throw_exception('ExpectedString',
                         '{0} is not a string'.format(cap_string))
     return cap_string.contents
Exemplo n.º 40
0
 def throw_select_on_empty_list():
     throw_exception('SelectOnEmptyList',
                     "Can't select item from empty list")
Exemplo n.º 41
0
def check_radix(radix):
    if (not is_int(radix)) or radix < 2 or radix > 36:
        throw_exception('UnsupportedBase', 'Base must be between 2 and 36 inclusive')
Exemplo n.º 42
0
 def execute(self, arg_values, env=None):
     if self.args is not None and len(arg_values) != len(self.args):
         throw_exception('IncorrectNumberOfArguments',
                         'Wrong number of arguments passed to built-in function')
     return self.action(*arg_values)
Exemplo n.º 43
0
def execute_lines(lines, env):
    """
    Executes lines of code in a given environment.
    """
    prgm_counter = 0
    cond_flag = False
    while prgm_counter < len(lines):
        line = lines[prgm_counter]
        directive = execute_statement(line, env)
        if directive is not None:
            if directive[0] == ':cond':
                cond_flag = eval_parentheses(directive[1], env)
                prgm_counter += 1
            elif directive[0] == ':j':
                prgm_counter = int(directive[1])
            elif (cond_flag and directive[0] == ':jt') or \
                 ((not cond_flag) and directive[0] == ':jf'):
                prgm_counter = int(directive[1])
            elif directive[0] == ':skiptoelse':
                # Nothing was thrown in this try clause
                _, j = find_next_end_else(lines, prgm_counter + 1, False)
                prgm_counter = j + 1
            elif directive[0] == ':hook':
                env.activate_hook(directive[1])
                prgm_counter += 1
            elif directive[0] == 'return':
                if directive[1] == 'this':
                    # Create a user-defined object.
                    # Do not use env.pop() because the function
                    # will automatically remove the stack frame
                    # upon completion.
                    obj = env.last()
                    # Allow the object to refer to itself
                    obj['this'] = obj
                    return obj
                else:
                    value = eval_parentheses(directive[1], env)
                    if str(value).startswith('<function'):
                        value = value.supply(env)
                    return value
            elif directive[0] == 'try':
                env.exception_push(prgm_counter)
                prgm_counter += 1
            elif directive[0] == 'throw':
                prgm_counter = env.exception_pop()
                if prgm_counter is None:
                    throw_exception('UncaughtException', 'Thrown value ' + str(directive[1:]))
                else:
                    original_counter = prgm_counter
                    # Look for a matching catch statement
                    prgm_counter = find_catch(directive, lines, prgm_counter, env)
                    if prgm_counter is None:
                        # We can't find a catch statement.
                        # Let the exception bubble up from its current scope.
                        env.exception_push(original_counter)
                        return Trigger(directive[1])
            elif directive[0] in ['catch', 'else']:
                kind, j = find_next_end_else(lines, prgm_counter + 1, True)
                # Travel to the next end
                prgm_counter = j + 1
            elif directive[0] == 'end':
                # This is an isolated end statement. Ignore it.
                prgm_counter += 1
            else:
                prgm_counter += 1
        else:
            prgm_counter += 1
Exemplo n.º 44
0
def assign_arguments(arg_names, arg_values, env):
    num_grouping_args = count_grouping_arguments(arg_names)
    if num_grouping_args == 0:
        if len(arg_values) > len(arg_names):
            throw_exception(
                'TooManyArgumentsException',
                'Number of arguments exceeds number expected in function definition'
            )
        for i in xrange(len(arg_names)):
            current_name = arg_names[i]
            if '=' in current_name:
                # This argument name has a default value given
                # TODO : the following line does not account for operators
                # such as ==, !=, <=, etc.
                pieces = [piece.strip() for piece in current_name.split('=')]
                if len(pieces) != 2:
                    throw_exception(
                        'DefaultArgumentException',
                        'Incorrect default argument syntax in {0}'.format(
                            current_name))
                var_name, var_expr = pieces
                if is_out_of_bounds(i, arg_values):
                    env.assign(var_name,
                               execution.eval_parentheses(var_expr, env))
                else:
                    # Instead of the default value, use the value already given
                    env.assign(var_name, arg_values[i])
            else:
                if is_out_of_bounds(i, arg_values):
                    throw_exception(
                        'ArgValueException',
                        'Number of arguments does not match function definition'
                    )
                else:
                    env.assign(current_name, arg_values[i])
    elif num_grouping_args == 1:
        for i in xrange(len(arg_names)):
            if is_grouping_argument(arg_names[i]):
                break
        # Remove brackets around group name
        group_name = arg_names[i][1:-1].strip()
        num_left = i
        num_right = len(arg_names) - num_left - 1
        # TODO : account for default arguments
        # (num_left + num_right) might be greater than the actual number of needed arguments
        if len(arg_values) < num_left + num_right:
            throw_exception(
                'TooFewArgumentsException',
                'Number of arguments is less than number expected in function definition'
            )
        else:
            values_for_left = arg_values[0:i]
            if num_right == 0:
                values_for_right = []
                values_for_middle = arg_values[i:]
            else:
                values_for_right = arg_values[-num_right:]
                values_for_middle = arg_values[i:-num_right]
            env.assign(group_name, values_for_middle)
            other_arg_names = arg_names[0:i] + arg_names[i + 1:]
            assign_arguments(other_arg_names,
                             values_for_left + values_for_right, env)
    else:
        throw_exception(
            'GroupingArgumentException',
            'Cannot have more than 1 grouping argument in function definition')
Exemplo n.º 45
0
def evaluate_operators(tokens, indices, env):
    """
    Evaluates an expression based on a list of tokens,
    indices where the operators are located, and
    environment env.
    """
    brackets = ['[', ']', '{', '}']
    for idx in indices:
        # TODO : checking bounds should no longer be
        # necessary after fixing the xrange issue.
        # (passing in an xrange as operator indices)
        if idx >= len(tokens):
            break
        op = tokens[idx]
        is_dot = op == '.'
        if idx > 0:
            left = convert_value(tokens[idx-1], env)
        else:
            left = None
        if idx + 1 >= len(tokens):
            # This index is not valid:
            break
        if not is_dot:
            right = convert_value(tokens[idx+1], env)
        else:
            right = tokens[idx+1]
        if left in brackets or right in brackets:
            break
        left, right = promote_values(left, right)
        if op == '+':
            tokens[idx-1 : idx+2] = [plus(left, right)]
        elif op == '-':
            if idx == 0:
                tokens[idx : idx+2] = [-right]
            else:
                tokens[idx-1 : idx+2] = [left - right]
        elif op == '*':
            tokens[idx-1 : idx+2] = [left * right]
        elif op == '/':
            # Todo allow for better ratio and int division
            tokens[idx-1 : idx+2] = [float(left) / float(right)]
        elif op == '%':
            tokens[idx-1 : idx+2] = [left % right]
        elif op == '^':
            tokens[idx-1 : idx+2] = [left ** right]
        elif op == ':':
            tokens[idx-1 : idx+2] = [Ratio(left, right)]
        elif op == '==':
            tokens[idx-1 : idx+2] = [left == right]
        elif op == '!=':
            tokens[idx-1 : idx+2] = [left != right]
        elif op == '>=':
            tokens[idx-1 : idx+2] = [left >= right]
        elif op == '<=':
            tokens[idx-1 : idx+2] = [left <= right]
        elif op == '>':
            tokens[idx-1 : idx+2] = [left > right]
        elif op == '<':
            tokens[idx-1 : idx+2] = [left < right]
        elif op == ' and ':
            tokens[idx-1 : idx+2] = [left and right]
        elif op == ' or ':
            tokens[idx-1 : idx+2] = [left or right]
        elif op == ' xor ':
            tokens[idx-1 : idx+2] = [(left and not right) or ((not left) and right)]
        elif op == 'not ':
            tokens[idx : idx+2] = [not right]
        elif op == '.':
            tokens[idx-1 : idx+2] = [left[right]]
    stage = 0
    while len(tokens) != 1:
        if stage == 0:
            tokens = evaluate_list(tokens, env)
            stage = 1
        elif stage == 1:
            tokens = index_lists(tokens, env)
            stage = 2
        elif stage == 2:
            input_tokens = tokens
            # TODO : avoid having to pass in an xrange of possible operator indices
            tokens = evaluate_operators(tokens, xrange(len(tokens) - 1), env)
            # If the value returned is no longer the outer list, it means
            # an inner list is the result:
            if tokens is not input_tokens:
                return tokens
            stage = 3
        elif stage == 3:
            throw_exception('ExprEval', str(tokens) + ' cannot be converted into a single value')
    # Return the resulting (single) value without the outer list.
    return convert_value(tokens[0], env)