def visitRepeat(self, ctx: BSParser.RepeatContext): # 'repeat value times' is translated in the IR as while (value > 0), with a decrement # appended to the end of the expression block; hence, # to ease translation later, we add a global for const(0) and const(1) if not self.symbol_table.get_global('CONST_0'): globalz = Symbol('CONST_0', 'global', ChemTypeResolver.numbers()) globalz.value = Number('CONST_0', 1, 0) self.symbol_table.add_global(globalz) if not self.symbol_table.get_global('CONST_1'): globalz = Symbol('CONST_1', 'global', ChemTypeResolver.numbers()) globalz.value = Number('CONST_1', 1, 1) self.symbol_table.add_global(globalz) self.visitChildren(ctx)
def visitMath(self, ctx: BSParser.MathContext): deff = self.visitVariableDefinition(ctx.variableDefinition()) symbol = Symbol(deff['name'], self.scope_stack[-1], ChemTypeResolver.numbers()) for use in ctx.primary(): var = self.visitPrimary(use) # This places any constants into the global symbol table. # By doing this, it makes it significantly easier to handle # arithmetic later in the compilation process. if 'value' in var.keys() and not self.symbol_table.get_global( var['name']): globalz = Symbol(var['name'], 'global', ChemTypeResolver.numbers()) globalz.value = Number(var['name'], 1, var['value']) self.symbol_table.add_global(globalz) if not ChemTypeResolver.is_number_in_set(var['types']): local = self.symbol_table.get_local(var['name']) if not local: raise UndefinedVariable("{} is not defined.".format( var['name'])) local.types.update(ChemTypeResolver.numbers()) if ChemTypes.UNKNOWN in local.types: local.types.remove(ChemTypes.UNKNOWN) self.symbol_table.update_symbol(local) self.symbol_table.add_local(symbol) return None
def visitBinops(self, ctx: BSParser.BinopsContext): op1 = self.visitPrimary(ctx.primary(0)) op2 = self.visitPrimary(ctx.primary(1)) # This places any constants into the global symbol table. # By doing this, it makes it significantly easier to handle # arithmetic later in the compilation process. if 'value' in op1.keys() and not self.symbol_table.get_global( op1['name']): globalz = Symbol(op1['name'], 'global', ChemTypeResolver.numbers()) globalz.value = Number(op1['name'], 1, op1['value']) self.symbol_table.add_global(globalz) if 'value' in op2.keys() and not self.symbol_table.get_global( op2['name']): globalz = Symbol(op2['name'], 'global', ChemTypeResolver.numbers()) globalz.value = Number(op2['name'], 1, op2['value']) self.symbol_table.add_global(globalz)
def visitExpressionList(self, ctx: BSParser.ExpressionListContext): args = list() for primary in ctx.primary(): arg = self.visitPrimary(primary) if 'value' in arg.keys() and self.symbol_table.get_global( "CONST_{}".format(arg['value'])) is None: const = Symbol('CONST_{}'.format(arg['value']), 'global', arg['value']) const.value = Number(const.name, 1, arg['value']) self.symbol_table.add_global(const) args.append(self.visitPrimary(primary)) return args
def visitNumberAssignment(self, ctx: BSParser.NumberAssignmentContext): deff = self.visitVariableDefinition(ctx.variableDefinition()) symbol = Symbol(deff['name'], self.scope_stack[-1], ChemTypeResolver.numbers()) self.symbol_table.add_local(symbol) # Yes, this is assigning value to something, # But this is a constant. So we know all # of the values up front. This also makes # the IRVisitor easier to work with. value = self.visitLiteral(ctx.literal()) const = Symbol('CONST_{}'.format(value), 'global', ChemTypeResolver.numbers()) const.value = Number('CONST_{}'.format(value), 1, value) self.symbol_table.add_global(const)
def visitNumberAssignment(self, ctx: BSParser.NumberAssignmentContext): deff = self.visitVariableDefinition(ctx.variableDefinition()) value = self.visitLiteral(ctx.literal()) size = 1 if deff['index'] == -1 else deff['index'] offset = deff['index'] if deff['index'] != size else -1 variable = Number(deff['name'], size, value) self.symbol_table.get_local(deff['name'], self.scope_stack[-1]).value = variable ir = Constant({'name': deff['name'], 'offset': offset, 'size': size, 'var': variable}, variable.value) self.current_block.add(ir) return None
def visitDetect(self, ctx: BSParser.DetectContext): """ Cases to consider: 1) a = dispense aaa x = detect mod on a 2) a[n] = dispense aaa x = detect mod on a[m] 3) a[n] = dispense aaa x = detect mod on a :param ctx: :return: """ deff = self.visitVariableDefinition(ctx.variableDefinition()) symbol = self.symbol_table.get_local(deff['name'], self.scope_stack[-1]) time_meta = None if ctx.timeIdentifier(): time = self.visitTimeIdentifier(ctx.timeIdentifier()) time_meta = TimeConstraint(IRInstruction.DETECT, time['quantity'], time['units']) module = self.symbol_table.get_global(ctx.IDENTIFIER().__str__()) use = self.visitVariable(ctx.variable()) use_var = self.symbol_table.get_local(use['name'], self.scope_stack[-1]) if symbol.value is None: symbol.value = Number(deff['name'], use_var.value.size) self.check_bounds({'index': use['index'], 'name': use_var.name, 'var': use_var.value}) # if use['index'] == -1: # use['index'] = use_var.value.size # if use['index'] == 0: # use['index'] = 1 # use_indices = list(use_var.value.value.keys()) ir = Detect({'name': deff['name'], 'offset': use['index'], 'size': symbol.value.size, 'var': symbol.value}, {'name': module.name, 'offset': 0, 'size': float("inf"), 'var': module}, {'name': use['name'], 'offset': use['index'], 'size': use_var.value.size, 'var': use_var.value}) if time_meta is not None: ir.meta.append(time_meta) self.current_block.add(ir) # for x in range(use['index']): # ir = Detect({"name": deff['name'], 'offset': x}, # {'name': module.name, 'offset': 0}, # {'name': use['name'], 'offset': use_indices[x]}) # if time_meta is not None: # ir.meta.append(time_meta) # self.current_block.add(ir) return None
def visitMath(self, ctx: BSParser.MathContext): deff = self.visitVariableDefinition(ctx.variableDefinition()) deff_var = self.symbol_table.get_local(deff['name'], self.scope_stack[-1]) deff_offset = 0 if deff['index'] == -1 else deff['index'] size = 1 if deff['index'] == -1 else deff['index'] # Has this variable been declared before? if deff_var.value is not None: self.check_bounds({ 'name': deff['name'], 'index': deff['index'], 'var': deff_var.value }) deff_var = deff_var.value else: deff_var = Number(deff['name'], size) self.symbol_table.get_local(deff['name'], self.scope_stack[-1]).value = deff_var # Check to see if this is a constant or a variable op1 = self.visitPrimary(ctx.primary(0)) if 'value' in op1.keys(): op1_var = self.symbol_table.get_global('CONST_{}'.format( op1['value'])).value else: op1_var = self.symbol_table.get_local(op1['name'], self.scope_stack[-1]).value self.check_bounds({ 'name': op1['name'], 'index': op1['index'], 'var': op1_var }) # Check to see if this is a constant or a variable op2 = self.visitPrimary(ctx.primary(1)) if 'value' in op2.keys(): op2_var = self.symbol_table.get_global('CONST_{}'.format( op2['value'])).value else: op2_var = self.symbol_table.get_local(op2['name'], self.scope_stack[-1]).value self.check_bounds({ 'name': op2['name'], 'index': op2['index'], 'var': op2_var }) # Set the offsets for everything. op1_offset = 0 if op1['index'] == -1 else op1['index'] op2_offset = 0 if op2['index'] == -1 else op2['index'] # Grab the operand. outcome = 0 if ctx.ADDITION(): operand = BinaryOps.ADD outcome = op1_var.value[op1_offset] + op2_var.value[op2_offset] elif ctx.SUBTRACT(): operand = BinaryOps.SUBTRACT outcome = op1_var.value[op1_offset] - op2_var.value[op2_offset] elif ctx.DIVIDE(): operand = BinaryOps.DIVIDE outcome = op1_var.value[op1_offset] / op2_var.value[op2_offset] elif ctx.MULTIPLY(): operand = BinaryOps.MULTIPLE outcome = op1_var.value[op1_offset] * op2_var.value[op2_offset] else: operand = BinaryOps.ADD outcome = op1_var.value[op1_offset] + op2_var.value[op2_offset] ir = Math( { 'name': deff['name'], 'offset': deff_offset, 'size': deff_var.size, 'var': deff_var }, { 'name': op1_var.name, 'offset': op1_offset, 'size': op1_var.size, 'var': op1_var }, { 'name': op2_var.name, 'offset': op2_offset, 'size': op2_var.size, 'var': op2_var }, operand) self.current_block.add(ir) if deff_var.value is None: deff_var.value = Number(deff['name'], value=outcome) return None
def visitRepeat(self, ctx: BSParser.RepeatContext): # get the (statically defined!) repeat value and add to local symbol table value = self.visitLiteral(ctx) val = { 'name': "REPEAT_{}".format(value), "index": 0, 'value': value, 'types': ChemTypeResolver.numbers() } if 'value' in val.keys() and not self.symbol_table.get_local( val['name']): localz = Symbol(val['name'], 'global', ChemTypeResolver.numbers()) localz.value = Number(val['name'], 1, val['value']) self.symbol_table.add_local(localz) # finished with this block self.functions[self.scope_stack[-1]]['blocks'][ self.current_block.nid] = self.current_block # insert header block for the conditional header_block = BasicBlock() header_label = Label("bsbbr_{}_h".format(header_block.nid)) self.labels[header_label.name] = header_block.nid header_block.add(header_label) self.graph.add_node(header_block.nid, function=self.scope_stack[-1], label=header_label.label) self.functions[self.scope_stack[-1]]['blocks'][ header_block.nid] = header_block self.graph.add_edge(self.current_block.nid, header_block.nid) zero = self.symbol_table.get_global('CONST_0') op = BinaryOp(left={ 'name': val['name'], 'offset': 0, 'size': 1, 'var': self.symbol_table.get_local(val['name']) }, right={ 'name': zero.name, 'offset': 0, 'size': 1, 'var': zero }, op=RelationalOps.GT) condition = Conditional( RelationalOps.GT, op.left, op.right) # Number('Constant_{}'.format(0), 1, 0)) header_block.add(condition) self.control_stack.append(header_block) # set up the true block true_block = BasicBlock() true_label = Label("bsbbr_{}_t".format(true_block.nid)) self.labels[true_label.name] = true_block.nid true_block.add(true_label) self.graph.add_node(true_block.nid, function=self.scope_stack[-1]) self.functions[self.scope_stack[-1]]['blocks'][ true_block.nid] = true_block condition.true_branch = true_label self.graph.add_edge(header_block.nid, true_block.nid) self.current_block = true_block self.visitBlockStatement(ctx.blockStatement()) # repeat is translated to a while loop as: while (exp > 0); # hence, we update exp by decrementing. one = self.symbol_table.get_global('CONST_1') ir = Math( { 'name': val['name'], 'offset': 0, 'size': 1, 'var': self.symbol_table.get_local(val['name']) }, { 'name': val['name'], 'offset': 0, 'size': 1, 'var': self.symbol_table.get_local(val['name']) }, { 'name': one.name, 'offset': 0, 'size': 1, 'var': one }, BinaryOps.SUBTRACT) self.current_block.add(ir) # the block statement may contain nested loops # If so, the current block is the last false block created for the inner-most loop # otherwise, the current block is the true_block created above # Either way, we can pop the control stack to find where to place the back edge # and immediately make the back edge (from 'current block' to the parent parent_block = self.control_stack.pop() self.graph.add_edge(self.current_block.nid, parent_block.nid) # we now deal with the false branch false_block = BasicBlock() false_label = Label("bsbbr_{}_f".format(false_block.nid)) self.labels[false_label.name] = false_block.nid false_block.add(false_label) condition.false_branch = false_label self.graph.add_edge(header_block.nid, false_block.nid) # We are done, so we need to handle the book keeping for # next basic block generation. self.graph.add_node(false_block.nid, function=self.scope_stack[-1]) self.functions[self.scope_stack[-1]]['blocks'][ false_block.nid] = false_block self.current_block = false_block return NOP()