Exemplo n.º 1
0
 def __init__(self, first, second):
     CompError.__init__(self,
                        "persist call at %s already defined as %s as %s",
                        second.lhs.location.start, first.lhs,
                        first.lhs.location.start)
     self.first = first
     self.second = second
Exemplo n.º 2
0
 def previsit_expression(self, expr, parent):
     expr.defined = parent.defined
     if isinstance(expr, ast.FunctionCall):
         op = OPTYPE_MAP.get(expr.name)
         if op is None:
             raise BadFunctionError(expr)
         expr.optype = op
         if expr.name == 'persist':
             if len(expr.args) != 1 or not isinstance(
                     expr.args[0], ast.ConstExpr):
                 raise CompError("Bad persist call at %s",
                                 expr.location.start)
             if not isinstance(parent, ast.AssignStmt):
                 raise PersistDeclarationError(expr)
             name = str(expr.args[0])
             if name in parent.persist:
                 raise PersistTwiceError(parent.persist[name], parent)
             # Global persist dictionary is now set for this function
             parent.persist[name] = parent
             parent.lhs.persist = name
     if isinstance(expr, ast.VarExpr):
         # TODO: "Declaration" doesn't really exist anymore.
         # Move this checking later to IR stage? So the same variable
         # can be defined in two branches and allowed there.
         decl = expr.defined.get(expr.var)
         if decl is None:
             raise UseBeforeDeclareError(expr.var, expr.location)
         expr.decl = decl
Exemplo n.º 3
0
def main():
    if len(sys.argv) != 2:
        print('Usage: %s [program]' % sys.argv[0])
        sys.exit(1)

    # Some rereouting I guess? So that 'print' uses stderr
    sys._stdout = sys.stdout  # pylint: disable=protected-access
    sys.stdout = sys.stderr

    source_name = sys.argv[1]
    if source_name == '-' and not sys.stdin.isatty():
        source = sys.stdin.read()
    elif os.path.exists(source_name):
        with open(source_name, 'r') as source_file:
            source = source_file.read()
    else:
        raise CompError("'%s' does not exist", source_name)

    bsl_codes = run(source)

    output = bsl_codes['command'].get_program()

    # Just always do this for now
    if source_name == '-' or True:
        sys._stdout.write(output)  # pylint: disable=protected-access
    else:
        prefix, _ = os.path.splitext(source_name)
        output_name = prefix + '.buf'
        with open(output_name, 'w') as output_file:
            output_file.write(output)
Exemplo n.º 4
0
 def extract_string(expr):
     if not isinstance(expr, ConstExpr):
         raise CompError('%s', expr)
     if isinstance(expr.value, str):
         return expr.value
     elif isinstance(expr.value, int):
         return str(expr.value)
     elif isinstance(expr.value, StringExpr):
         pieces = []
         for piece in expr.value.pieces:
             if isinstance(piece, str):
                 pieces.append(piece)
             else:
                 # Do this in typechecking as well probably
                 raise CompError('Expected pure string, got %s', expr)
         return ''.join(pieces)
     else:
         raise CompInternalError(expr)
Exemplo n.º 5
0
 def postvisit_expression(self, expr, children):
     if isinstance(expr, ast.VarExpr):
         # This wll just be int
         # TODO: Boolean variables
         expr.typ = expr.decl.typ
     if isinstance(expr, ast.FlagExpr):
         # TODO: Should do this in declare?
         flagtype = FLAG_MAP.get(expr.var)
         if flagtype is None:
             # TODO: Location?
             raise CompError('Bad flag %s', expr)
         expr.typ = flagtype
     elif isinstance(expr, ast.ConstExpr):
         if isinstance(expr.value, bool):
             expr.typ = BaseType.BOOL
         elif isinstance(expr.value, int):
             expr.typ = MetaType.INT_OR_STRING
         else:
             expr.typ = BaseType.STRING
     elif isinstance(expr, ast.StringExpr):
         # TODO: Make a complex string vs non-complex string type?
         # Just so complex strings can't be compared with each other
         expr.typ = BaseType.STRING
     elif isinstance(expr, ast.UnaryExpr):
         op = OPTYPE_MAP.get(expr.op)
         if op is None:
             raise CompError('Internal error. No op called %s', expr)
         expr.optype = op
         expr.typ = self.require_func(expr, [expr.arg])
     elif isinstance(expr, ast.BinaryExpr):
         op = OPTYPE_MAP.get(expr.op)
         if op is None:
             raise CompError('Internal error. No op called %s', expr)
         expr.optype = op
         expr.typ = self.require_func(expr, [expr.lhs, expr.rhs])
     elif isinstance(expr, ast.FunctionCall):
         # optyp gets assigned in declaration visiting
         expr.typ = self.require_func(expr, expr.args)
Exemplo n.º 6
0
 def require(self, expr, typ):
     # INT_OR_STRING should get collapsed into INT or STRING (preferring the former)
     # I believe this always happen at the usage site, so we don't need weird
     # constraint-solving complexity.
     if expr.typ == MetaType.INT_OR_STRING or typ == MetaType.INT_OR_STRING:
         resolved = self.resolve_intstring(expr.typ, typ)
         if not resolved:
             raise TypeMismatchError(expr, typ)
         return resolved
     if expr.typ == MetaType.SAME_TYPE or typ == MetaType.SAME_TYPE:
         raise CompError('Internal error: %s, %s', expr, typ)
     if expr.typ != typ:
         raise TypeMismatchError(expr, typ)
     return expr.typ
Exemplo n.º 7
0
def run(source):
    parser.yacc.has_error = False
    ast_p = parser.yacc.parse(source, tracking=True)
    if parser.yacc.has_error:
        raise CompError("Invalid program")
    #print repr(ast_p)

    ast.visit_ast(declare.DeclarationVisitor(), ast_p)
    ast.visit_ast(typecheck.TypeVisitor(), ast_p)

    conv = ast_codegen.AstCodegen()
    ir_program = conv.generate(ast_p)
    ir_program.merge_empty_blocks()
    ir_program.remove_dead_blocks()

    # TODO: This.
    elems = []  # dataflow.get_undefined(ir_program)
    if len(elems):
        elem = elems[0]
        raise CompError(
            'Variable "%s" is used at %s without being defined first', elem, elem.location.start)
    reg_program = registers.allocate_registers(ir_program)

    return bsl_codegen.output_bsl_codes(reg_program)
Exemplo n.º 8
0
 def resolve_intstring(typ1, typ2):
     if typ1 == MetaType.INT_OR_STRING:
         if typ2 not in [
                 BaseType.INT, BaseType.STRING, MetaType.INT_OR_STRING
         ]:
             return None
         return BaseType.INT if typ2 == MetaType.INT_OR_STRING else typ2
     elif typ2 == MetaType.INT_OR_STRING:
         if typ1 not in [
                 BaseType.INT, BaseType.STRING, MetaType.INT_OR_STRING
         ]:
             return None
         return BaseType.INT if typ1 == MetaType.INT_OR_STRING else typ1
     else:
         raise CompError('Internal error: %s, %s', typ1, typ2)
Exemplo n.º 9
0
 def require_func(self, expr, args):
     desc = ('Call %s at %s' %
             (expr.name, expr.location.start)) if isinstance(
                 expr, ast.FunctionCall) else ('%s' % expr.op)
     optype = expr.optype
     ndecl, ncall = len(optype.args), len(args)
     if ndecl != ncall:
         diff = 'too %s parameters (saw %d, require %d)' % (
             'many' if ndecl < ncall else 'few', ncall, ndecl)
         raise CompError('%s has %s', desc, diff)
     sametypes = []
     for decltype, callexpr in zip(optype.args, args):
         if decltype == MetaType.SAME_TYPE:
             sametypes.append(callexpr)
         else:
             callexpr.typ = self.require(callexpr, decltype)
     # Fake polymorphism
     # This doesn't work anymore, we can compare string expr vs string const
     # TODO: Make a table?
     if len(sametypes):
         # Just == and != for now
         if len(sametypes) != 2:
             raise CompError('Internal error: %s, %s', expr, args)
         s1, s2 = sametypes  # pylint: disable=unbalanced-tuple-unpacking
         resolved = None
         if s1.typ == MetaType.INT_OR_STRING or s2.typ == MetaType.INT_OR_STRING:
             # May return None
             resolved = self.resolve_intstring(s1.typ, s2.typ)
         elif s1.typ == s2.typ:
             resolved = s1.typ
         if resolved is None:
             raise CompError(
                 '%s needs two compatible types; found %s and %s', desc,
                 s1.typ, s2.typ)
         s1.typ = s2.typ = resolved
     return optype.ret
Exemplo n.º 10
0
 def gen_target(self, expr):
     """
     Returns the target (variable or constant) corresponding to an expression
     If no target exists, a temporary variable is generated.
     No CSE is performed at this stage.
     """
     if isinstance(expr, VarExpr):
         # Should use declnum to distinguish scoped things
         # Useful for loops and stuff later
         if hasattr(expr.decl, 'persist'):
             return PersistVariable(expr.decl.persist, expr.var, expr.location)
         else:
             return Variable(expr.var, expr.location, 0)
     elif isinstance(expr, FlagExpr):
         return FlagVariable(expr.var, expr.location)
     elif isinstance(expr, ConstExpr):
         if isinstance(expr.value, StringExpr):
             return self.gen_pieces(expr.value.pieces)
         # TODO: Does this case happen?
         if isinstance(expr.value, str):
             return StringConstant(expr.value)
         return Constant(int(expr.value))
     elif isinstance(expr, BinaryExpr):
         # This shares a common routine with expr
         tmp = self.gen_temp()
         self.gen_assignment(expr, tmp)
         return tmp
     elif isinstance(expr, UnaryExpr):
         if isinstance(expr.arg, ConstExpr):
             if expr.op == UnaryOp.NOT:
                 return Constant(int(not expr.arg.value))
             else:
                 return Constant(-expr.arg.value)
         tmp = self.gen_temp()
         self.gen_assignment(expr, tmp)
         return tmp
     elif isinstance(expr, FunctionCall):
         tmp = self.gen_temp()
         self.gen_call(expr, tmp)
         return tmp
     else:
         raise CompError('Unknown expression %s', expr)
Exemplo n.º 11
0
 def previsit_statement(self, stmt, parent, prev_stmt):
     stmt.defined = parent.defined
     if prev_stmt is not None:
         stmt.defined = prev_stmt.defined
     stmt.persist = parent.persist
     # TODO: Check ~ LiteralStmt actually calls a command, rather than whitespace
     if isinstance(stmt, ast.AssignStmt):
         # TODO: Remove this once registers.py is fully implemented.
         if re.match(TMPVAR_RE, stmt.lhs.var):
             raise CompError(
                 'Invalid variable name %s (tmp plus numbers), '
                 'until full IR renaming exists', stmt.lhs)
         stmt.defined = stmt.defined.copy()
         stmt.lhs.typ = BaseType.INT
         prevdecl = stmt.defined.get(stmt.lhs.var)
         if prevdecl is not None and hasattr(prevdecl, 'persist'):
             stmt.lhs.persist = prevdecl.persist
         # TODO: Move this to visiting the expression. ??
         stmt.defined[stmt.lhs.var] = stmt.lhs
     elif isinstance(stmt, ast.LiteralStmt) and stmt.op == LiteralOp.CALL:
         pass
Exemplo n.º 12
0
    def gen_conditional(self, expr, negate=False):
        """
        This is a specialization of gen_target when dealing with conditional
        expressions (i.e., ones used for branching).

        Emits a chain of blocks necessary to evaluate the conditional expression,
        the last of which will be a new block which is only reached if all of these
        blocks are successfully successfully passed through.

        When this function returns, the current block will be what happens if
        the overall condition is true, and the return value will be the list of
        blocks which need to be given a false branch if the overall condition
        is false at any stage.
        """
        # If a BooleanExpr, we are not generating CondInstrs, only gluing them together.
        # TODO: Rewrite all of this probably. Currently unreachable due to AST limits.
        if isinstance(expr, BinaryExpr) and expr.op in boolean_ops:
            # Tricky bit: if negating the expression, use the other kind of BinaryOp
            # and negate both lhs and rhs. This is just DeMorgan's law.
            # Note: negate is not currently used, but was previously, and doesn't add
            # much complexity.
            if (expr.op == BinaryOp.AND) != negate:
                #if (lhs && rhs) then [true branch] else [false branch]
                #if lhs then (if rhs then [true branch] else [false branch]) else [false branch]
                lblocks = self.gen_conditional(expr.lhs, negate)
                rblocks = self.gen_conditional(expr.rhs, negate)
                # If a block in lhs or in rhs is false, the overall expression will be false.
                false_blocks = lblocks + rblocks
            elif (expr.op == BinaryOp.OR) != negate:
                #if (lhs || rhs) then [true branch] else [false branch]
                #if lhs then [true branch] else (if rhs then [true branch] else [false branch])
                lblocks = self.gen_conditional(expr.lhs, negate)
                empty_true_block = self.current_block()
                rhs_block = self.new_block()
                rblocks = self.gen_conditional(expr.rhs, negate)
                true_block = self.current_block()
                # If a block in lhs is false, the overall expression depends on the rhs.
                for block in lblocks:
                    block.add_successor(rhs_block)
                # If all blocks in lhs are true, the overall expression will be true.
                # This requires an empty block, but it'll be merged.
                empty_true_block.add_successor(true_block)
                # If a block in rhs is false, the overall expression will be false.
                false_blocks = rblocks
            return false_blocks
        else:
            if isinstance(expr, BinaryExpr) and expr.op in comparison_ops:
                op = expr.op
                args = [expr.lhs, expr.rhs]
            elif isinstance(expr, FunctionCall) and expr.name == 'isint':
                op = 'isint'
                args = [expr.args[0]]
            else:
                raise CompError('Cannot do comparison %s', expr)

            op = opposite_comparison[op] if negate else op
            target_args = [self.gen_target(arg) for arg in args]
            self.append(CondInstr(op, target_args))

            cond_block = self.current_block()
            true_block = self.new_block()
            cond_block.add_successor(true_block)
            return [cond_block]
Exemplo n.º 13
0
    def gen_call(self, expr, target):
        optype = expr.optype
        args = expr.args
        if target is None and optype.name != 'sleep':
            # Could allow this, but it is a code smell as well
            raise CompError('No assigned target for %s', expr)
        if optype.name == 'persist':
            # All persist variables are already marked
            # TODO: Make sure they are...
            return
        # If it's being called from here, it's outside of the context of
        # a conditional - it's being assigned to a variable.
        elif optype.name == 'isint':
            # Some duplication with gen_conditional, but we do not
            # need multiple blocks.
            # First, assume it's false
            arg_target = self.gen_target(args[0])
            self.append(MoveInstr(Constant(int(False)), target))
            self.append(CondInstr('isint', [arg_target]))
            cond_block = self.current_block()

            # Set it to true in the true block
            true_block = self.new_block(cond_block)
            self.append(MoveInstr(Constant(int(True)), target))

            # Then, everything goes to the end block.
            self.new_block(true_block, cond_block)
        elif optype.name == 'parseint':
            # For now, explicit quit.
            # BSL seems to do something similar.
            arg_target = self.gen_target(args[0])
            self.append(CondInstr('isstring', [arg_target]))
            cond_block = self.current_block()

            # Panic in the true block
            true_block = self.new_block(cond_block)
            self.exits.append(true_block)

            # Otherwise, go to end block.
            self.new_block(cond_block)
            self.append(MoveInstr(arg_target, target))
        else:
            # The rest are just normal FuncInstrs
            if optype.name == 'rand':
                func_name = 'rng'
                targets = [args[0].lhs, args[0].rhs]
            elif optype.name == 'sleep':
                func_name = 'slp'
                targets = [args[0]]
            elif optype.name == 'time':
                func_name = 'tme'
                targets = []
            elif optype.name == 'hash':
                func_name = 'fpt'
                # Last arg[2] should either be int constant or string constant.
                # And, do transformation here.
                if isinstance(args[2].value, int):
                    seed = args[2].value
                else:
                    s = self.extract_string(args[2])
                    seed = hashimpl.murmurhash3_32(s)
                targets = [args[1].lhs, args[1].rhs, args[0], ConstExpr(seed)]
            else:
                raise CompError('Unknown function in %s', expr)
            arg_targets = []
            for arg in targets:
                arg_target = self.gen_target(arg)
                arg_targets.append(arg_target)
            self.append(FuncInstr(func_name, arg_targets, target))
Exemplo n.º 14
0
    def gen_assignment(self, expr, target):
        """
        After these instructions are emitted, target will be
        assigned to the value of the expression.
        Target must be a Variable or TmpVar.
        """
        if isinstance(expr, (VarExpr, FlagExpr, ConstExpr)):
            # TODO Special case: persist calls
            var = self.gen_target(expr)
            self.append(MoveInstr(var, target))
        elif isinstance(expr, BinaryExpr):
            # TODO: Add a negate argument here and use it here
            # This section should NOT call gen_assignment on expr, only subexprs
            # Because gen_target calls this
            if expr.op in [BinaryOp.AND, BinaryOp.OR]:
                stor = target
                # Boolean expressions are normally generated as:
                # z = x && y: [ z = x; if (z != 0) z = y; ]
                # This does not work when z is modified in x, so we use a temporary instead.
                # For, example: x = 0; x = (y = x++) || x;
                # Make sure this works for static variables - reason that this is fine is
                # that we can have some global x for the above example, so we need to do the same
                # assign to temp and store back in location
                # ... Don't do this?
                #if (isinstance(target, Variable) or isinstance(target, StaticVariable)) and target.name in expr.assigns_vars:
                #    stor = self.gen_temp()

                self.gen_assignment(expr.lhs, stor)
                # Skip over the secondary instruction if neccesary
                if expr.op == BinaryOp.AND:
                    #if x != 0 then y else x
                    self.append(CondInstr(BinaryOp.NOTEQUAL, [stor, CondInstr.ZERO]))
                elif expr.op == BinaryOp.OR:
                    #if x == 0 then y else x
                    self.append(CondInstr(BinaryOp.ISEQUAL, [stor, CondInstr.ZERO]))
                block = self.current_block() # in case the lhs generated new blocks
                noshort = self.new_block()
                block.add_successor(noshort)
                # This is the fallthrough (non-short-circuit) branch
                # We gen another assignment because we want the result to be in the same
                # location as that assignment, because the result is in that location
                self.gen_assignment(expr.rhs, stor)

                # And now the short-circuit and non-short-circuit branches point to the
                # block after
                noshort_end = self.current_block()
                after = self.new_block()
                block.add_successor(after)
                noshort_end.add_successor(after)
                if stor is not target:
                    self.append(MoveInstr(stor, target))
            elif expr.op == BinaryOp.RANGE:
                raise CompError('Cannot generate assignment for %s', expr)
            else:
                t1 = self.gen_target(expr.lhs)
                t2 = self.gen_target(expr.rhs)
                self.append(OpInstr(expr.op, target, [t1, t2]))
        elif isinstance(expr, UnaryExpr):
            # Unary expressions are a convenience of the language that get wiped before BSL.
            if isinstance(expr.arg, ConstExpr):
                if expr.op == UnaryOp.NOT:
                    value = int(not expr.arg.value)
                else:
                    value = -expr.arg.value
                self.append(MoveInstr(value, target))
            elif expr.op == UnaryOp.NOT:
                t1 = self.gen_target(expr.arg)
                self.append(OpInstr(BinaryOp.MINUS, target, [Constant(1), t1]))
            else:
                t1 = self.gen_target(expr.arg)
                self.append(OpInstr(BinaryOp.MINUS, target, [Constant(0), t1]))
        elif isinstance(expr, FunctionCall):
            self.gen_call(expr, target)
        else:
            raise CompError('Unknown expression %s', expr)
Exemplo n.º 15
0
    def gen_statement(self, stmt):
        if isinstance(stmt, ConditionalStmt): # hello
            self.gen_if(stmt.condition, stmt.truestmt, stmt.falsestmt)
        elif isinstance(stmt, LoopStmt):
            self.gen_loop(stmt.condition, stmt.statements)
        elif isinstance(stmt, AssignStmt):
            if isinstance(stmt.lhs, VarExpr):
                var = self.gen_target(stmt.lhs)
                self.gen_assignment(stmt.rhs, var)
        elif isinstance(stmt, FunctionStmt):
            self.gen_call(stmt.function, None)
        elif isinstance(stmt, PostStmt):
            var = self.gen_target(stmt.arg)
            self.gen_incdec(stmt.incdec, var)
        elif isinstance(stmt, SwitchStmt):
            var = self.gen_target(stmt.arg)
            # List of (list of (op, value), statements)
            cases = []
            default = None
            # TODO: Optimize int better. Don't need bottom range if
            # all ranges are adjacent, and can do binary search even for large
            # numbers of cases, given 50 instruction limit.
            if stmt.typ not in [BaseType.INT, BaseType.STRING]:
                raise CompInternalError(stmt)
            for case in stmt.cases:
                anyof = []
                if not case.vals:
                    default = case.statements
                    continue
                for val in case.vals:
                    if stmt.typ == BaseType.STRING:
                        strval = self.extract_string(val)
                        anyof.append((BinaryOp.ISEQUAL, strval))
                    else:
                        if isinstance(val, ConstExpr):
                            anyof.append((BinaryOp.ISEQUAL, int(val.value)))
                        elif isinstance(val, BinaryExpr) and val.op == BinaryOp.RANGE:
                            anyof.append((BinaryOp.INRANGE, (int(val.lhs.value), int(val.rhs.value))))
                        else:
                            raise CompInternalError(stmt)
                cases.append((anyof, case.statements))
            # Generate all cases, then generate all statements
            # There are some weird inverted conditions here, mainly used so that
            # the expected branch is false, so it occurs directly after.
            # List of (list of blocks going to statements when true, statements)
            statement_blocks = []
            for conds, statements in cases:
                # Blocks going to statement when true
                case_statement_blocks = []
                for op, val in conds:
                    # If any blocks are false, go to the next cond.
                    # If all blocks are true, go to statements.
                    if op == BinaryOp.ISEQUAL:
                        const = StringConstant(val) if isinstance(val, str) else Constant(val)
                        self.append(CondInstr(BinaryOp.ISEQUAL, [var, const]))
                        check_block = self.current_block()

                        self.new_block(check_block, branch=False)
                        case_statement_blocks.append(check_block)
                    elif op == BinaryOp.INRANGE:
                        bottom, top = val
                        # If out of bottom range, go to next cond. Else, check upper count.
                        # TODO: This is optional if the ranges are adjacent.
                        self.append(CondInstr(BinaryOp.GREATERTHAN, [var, Constant(top)]))
                        first_check = self.current_block()

                        second_check = self.new_block(first_check, branch=False)
                        self.append(CondInstr(BinaryOp.GREATEREQ, [var, Constant(bottom)]))

                        next_block = self.new_block(second_check, branch=False)
                        first_check.add_successor(next_block, branch=True)
                        case_statement_blocks.append(second_check)
                statement_blocks.append((case_statement_blocks, statements))
            end_blocks = []
            if default:
                # If no other conditions get met, do the default action.
                self.gen_statements(default)
            # The default action (even if it's nothing) goes to the end.
            end_blocks.append(self.current_block())
            for blocks, statements in statement_blocks:
                self.new_block(*blocks, branch=True)
                self.gen_statements(statements)
                end_blocks.append(self.current_block())
            # New block where everything combines
            self.new_block(*end_blocks)

        #collections.namedtuple('SwitchStmt', ['arg', 'cases']),
        #collections.namedtuple('CaseStmt', ['statements', 'vals']),
        #collections.namedtuple('BinaryExpr', ['op', 'lhs', 'rhs']),
        elif isinstance(stmt, LiteralStmt):
            pieces = self.gen_pieces(stmt.pieces)
            self.append(LiteralInstr(stmt.op, pieces))
        elif isinstance(stmt, QuitStmt):
            # TODO: Should check that there aren't unreachable statements.
            self.exits.append(self.current_block())
            self.new_block()
        else:
            raise CompError('Unknown statement %s', stmt)
Exemplo n.º 16
0
 def __init__(self, expr, typ):
     # These errors could be more descriptive, but one-size-fits-all is good for now.
     CompError.__init__(self, 'Expression at %s has type %s; expected %s',
                        expr.location, expr.typ, typ)
     self.expr = expr
     self.typ = typ
Exemplo n.º 17
0
 def __init__(self, call):
     CompError.__init__(self, "Function %s called at %s does not exist",
                        call.name, call.location.start)
     self.call = call
Exemplo n.º 18
0
 def __init__(self, var, location):
     CompError.__init__(
         self, "Variable %s at %s used without being defined first", var,
         location.start)
     self.var = var
     self.location = location
Exemplo n.º 19
0
 def __init__(self, call):
     CompError.__init__(
         self,
         "persist(\"%s\") call at %s not assigned directly to a variable",
         call.args[0], call.location.start)
     self.call = call