def visitMix(self, ctx: BSParser.MixContext): deff = self.visitVariableDefinition(ctx.variableDefinition()) symbol = Symbol(deff['name'], self.scope_stack[-1], self.resolve_types(deff)) # Look through the RHS vars. for fluid in ctx.variable(): temp = self.visitVariable(fluid) var = self.symbol_table.get_local(temp['name']) if not var: raise UndefinedVariable("{}.{} is not defined.".format( self.scope_stack[-1], temp['name'])) if not ChemTypeResolver.is_mat_in_set(var.types): # This is the best we can do at this point. # We won't be able to further classify anything # further because if the identifier hasn't # figured anything out by now, it won't. var.types.update( self.resolve_types({ 'name': var.name, 'types': var.types })) # Update the RHS types. self.symbol_table.update_symbol(var) # Union the types of the RHS with the LHS symbol.types.update(var.types) # Add the symbol to the table. self.symbol_table.add_local(symbol) return None
def visitGradient(self, ctx: BSParser.GradientContext): deff = self.visitVariableDefinition(ctx.variableDefinition()) symbol = Symbol(deff['name'], self.scope_stack[-1], self.resolve_types(deff)) for var_def in ctx.variable(): use = self.visitVariable(var_def) var = self.symbol_table.get_local(use['name'], self.scope_stack[-1]) if not var: raise UndefinedVariable("{} is not defined.".format( use['name'])) if not ChemTypeResolver.is_mat_in_set(var.types): var.types.add(ChemTypes.MAT) self.symbol_table.update_symbol(var) symbol.types.update(var.types) self.symbol_table.add_local(symbol) start = float(ctx.FLOAT_LITERAL(0).__str__()) end = float(ctx.FLOAT_LITERAL(1).__str__()) at = float(ctx.FLOAT_LITERAL(2).__str__()) if start >= end: raise UnsupportedOperation( "The beginning concentration must be smaller than the ending concentration." ) if at <= 0.0: raise UnsupportedOperation("You cannot have a negative rate.") return None
def visitFormalParameter(self, ctx: BSParser.FormalParameterContext): if ctx.unionType(): types = self.visitUnionType(ctx.unionType()) else: types = {ChemTypes.UNKNOWN} var = ctx.IDENTIFIER().__str__() symbol = Symbol(var, self.scope_stack[-1], types) self.symbol_table.add_local(symbol) return symbol
def visitDispense(self, ctx: BSParser.DispenseContext): deff = self.visitVariableDefinition(ctx.variableDefinition()) deff['types'].update( self.symbol_table.get_global(ctx.IDENTIFIER().__str__()).types) self.symbol_table.add_local( Symbol(deff['name'], self.scope_stack[-1], self.resolve_types(deff))) if not self.symbol_table.get_global(ctx.IDENTIFIER().__str__()): raise UndefinedVariable( "{} isn't declared in the manifest.".format( ctx.IDENTIFIER().__str__())) return None
def visitSplit(self, ctx: BSParser.SplitContext): deff = self.visitVariableDefinition(ctx.variableDefinition()) use = self.visitVariable(ctx.variable()) if not self.symbol_table.get_local(use['name']): raise UndefinedVariable("{} is not defined.".format(use['name'])) if not ChemTypeResolver.is_mat_in_set(deff['types']): deff['types'].update( self.identifier.identify(deff['name'], deff['types'])) if ChemTypes.UNKNOWN in deff['types']: deff['types'].remove(ChemTypes.UNKNOWN) deff['types'].update(self.symbol_table.get_local(use['name']).types) self.symbol_table.add_local( Symbol(deff['name'], self.scope_stack[-1], deff['types'])) return None
def visitMethodInvocation(self, ctx: BSParser.MethodInvocationContext): deff = self.visitVariableDefinition(ctx.variableDefinition()) method_name, args = self.visitMethodCall(ctx.methodCall()) if len(args) != len(self.symbol_table.functions[method_name].args): raise UnsupportedOperation( "Function {} takes {} arguments; {} arguments provided.". format(method_name, len(self.symbol_table.functions[method_name].args), len(args))) symbol = Symbol(deff['name'], self.scope_stack[-1], self.symbol_table.functions[method_name].types) self.symbol_table.add_local(symbol) return None
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 visitDetect(self, ctx: BSParser.DetectContext): deff = self.visitVariableDefinition(ctx.variableDefinition()) self.symbol_table.add_local( Symbol(deff['name'], self.scope_stack[-1], ChemTypeResolver.numbers())) use = self.visitVariable(ctx.variable()) var = self.symbol_table.get_local(use['name']) if not var: raise UndefinedVariable("{} is not defined.".format(use['name'])) module = self.symbol_table.get_global(ctx.IDENTIFIER().__str__()) if not module: raise UndefinedVariable( "{} isn't declared in the manifest.".format( ctx.IDENTIFIER().__str__())) if ChemTypes.MODULE not in module.types: raise UndefinedVariable( "There is no module named {} declared in the manifest.".format( module.name)) if not ChemTypeResolver.is_mat_in_set(var.types): var.types.add(ChemTypes.MAT) self.symbol_table.update_symbol(var) 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 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()