from functools import reduce from .Exceptions import BFSemanticError, BFSyntaxError from .Token import Token """ This file holds functions that generate general Brainfuck code And general functions that are not dependent on other objects """ # ================= # Brainfuck code # ================= def get_set_cell_value_code(new_value, previous_value, zero_next_cell_if_necessary=True): # this function returns a code that sets the current cell's value to new_value, # given that its previous value is previous_value # it may return the "naive" way, of "+"/"-" usage, times # and it may return an optimization using loops, by using the next cell as a loop counter # if zero_next_cell_if_necessary is set to False, it assumes that the next cell is already 0 # after the code of this function is executed, the pointer will point to the original cell # this function returns the shorter code between "naive" and "looped" def get_char(value): return "+" if value > 0 else "-" offset = new_value - previous_value char = get_char(offset) is_negative = offset < 0 offset = abs(offset) # "naive" code is simply +/-, times naive = char * offset # "looped" code is "[ times perform adds/subs] and then more adds/subs" def get_abc(offset): # returns a,b,c such that a*b+c=offset and a+b+c is minimal min_a, min_b, min_c = offset, 1, 0 min_sum = offset + 1 left = 1 right = offset // 2 - 1 while right >= left: a, b = left + 1, right + 1 c = offset - a * b curr_sum = abs(a) + abs(b) + abs(c) if curr_sum < min_sum: min_a, min_b, min_c = a, b, c min_sum = curr_sum if a * b > offset: right -= 1 else: left += 1 return min_a, min_b, min_c a, b, c = get_abc(offset) looped = ">" # point to next cell (loop counter) if zero_next_cell_if_necessary: looped += "[-]" # zero it if necessary looped += "+" * a # set loop counter looped += "[<" + char * abs(b) + ">-]" # sub 1 from counter, perform b actions looped += "<" # point to "character" cell looped += get_char(-c if is_negative else c) * abs(c) # c more actions if len(naive) > len(looped): return looped else: return naive def get_move_to_offset_code(offset): # returns code that moves value from current pointer to cell at offset to the left # after this, the pointer points to the original cell, which is now the next available cell code = "<" * offset # point to destination code += "[-]" # zero destination code += ">" * offset # point to source cell code += "[" + "<" * offset + "+" + ">" * offset + "-]" # increase destination, zero source # point to next free location (source, which is now zero) return code def get_copy_to_offset_code(offset): # returns code that copies value from current pointer to cell at offset to the left # after this, the pointer points to the original cell, which remains unchanged code = ">" # point to temp code += "[-]" # zero temp code += "<" * (offset + 1) # point to destination code += "[-]" # zero destination code += ">" * offset # point to source cell code += "[>+" + "<" * (offset + 1) + "+" + ">" * offset + "-]" # increase temp and destination, zero source code += ">" # point to temp code += "[<+>-]" # move temp to original cell code += "<" # point to original cell return code def get_copy_to_variable_code(ids_map_list, ID_token, current_pointer): # returns code that copies value from current pointer to cell of the variable ID # after this, the pointer points to the original cell, which remains unchanged offset = get_offset_to_variable(ids_map_list, ID_token, current_pointer) return get_copy_to_offset_code(offset) def get_move_to_return_value_cell_code(return_value_cell, current_stack_pointer): # returns code that moves value from current pointer to return_value cell # after this, the pointer points to the original cell, which is now the next available cell # we need to move it cells left return get_move_to_offset_code(current_stack_pointer - return_value_cell) def unpack_multidimensional_literal_tokens_to_array_dimensions(ID_token, array_dimensions, literal_tokens_list): if len(array_dimensions) == 0: raise BFSemanticError("Tried to initialize array %s with too many nested sub-arrays" % ID_token) if len(literal_tokens_list) > array_dimensions[0]: raise BFSemanticError( "Tried to initialize array %s dimension %s with too many elements (%s)" % (ID_token, str(array_dimensions), str(len(literal_tokens_list))) ) result = [] for element in literal_tokens_list: if isinstance(element, list): # recursively unpack the list with the sub-dimension of the sub-array # E.g if we have arr[3][3][3] and then this call will fill [3][3]=9 elements result.extend( unpack_multidimensional_literal_tokens_to_array_dimensions(ID_token, array_dimensions[1:], element) ) else: result.append(element) if len(array_dimensions) > 1: dimension_size = dimensions_to_size(array_dimensions[1:]) # current size we need to fill result.extend( [Token(Token.NUM, 0, 0, "0")] * (dimension_size - 1) ) # fill missing elements in this dimension with zeros dimension_size = dimensions_to_size(array_dimensions) # current size we need to fill result.extend([Token(Token.NUM, 0, 0, "0")] * (dimension_size - len(result))) # fill the result with zeros return result def unpack_literal_tokens_to_array_dimensions(ID_token, array_dimensions, literal_tokens_list): # gets array dimensions and list of (list of list of...) literal tokens to initialize it with # returns one long list of literal tokens that can be used to initialize the array as a one dimensional array # if there are missing literals to fill the entire array, then fill the blanks with NUM 0 # E.g if the code is int arr[3][3][3] = {{1,2,3}, {}, {7, 8}} # Then this function receives ([3,3,3] and [[1,2,3],[],[7,8]]) and returns [1,2,3,0,0,0,7,8,0] (all are tokens) array_size = dimensions_to_size(array_dimensions) # current size we need to fill if all(not isinstance(element, list) for element in literal_tokens_list): # special case - if all elements are literals, then we allow assigning them as-is and not care about dimensions # E.g if we have arr[3][3][3] = {1,2,3,4} then return [1,2,3,4,0,0,0,0,0] unpacked_literals_list = literal_tokens_list + [Token(Token.NUM, 0, 0, "0")] * ( array_size - len(literal_tokens_list) ) # fill missing with zeros else: unpacked_literals_list = unpack_multidimensional_literal_tokens_to_array_dimensions( ID_token, array_dimensions, literal_tokens_list ) if len(unpacked_literals_list) > array_size: raise BFSemanticError( "Tried to initialize array %s with incompatible amount of literals." " (array size is %s and literals size is %s)" % (ID_token, str(array_size), str(len(unpacked_literals_list))) ) assert len(unpacked_literals_list) == array_size return unpacked_literals_list def process_switch_cases(expression_code, cases): # This function receives expression_code (string) and cases (list of tuples) corresponding to switch cases # Each tuple is (case_value, case_code, has_break) # And it returns code for the switch-case statement (string) if len(cases) == 0: code = ">" # point to next cell code += expression_code # evaluate expression code += "<" # point to expression code += "<" # discard result return code def process_cases(cases): # This function gets the cases list of tuples # And returns 2 values: default_code (string), all_cases_have_break (bool) # Note - default_code includes code of all relevant cases that are after the default case (if there's no break) all_cases_have_break = all(has_break for (_, _, has_break) in cases) has_default, default_code = False, "" for case, case_code, has_break in cases: if case == "default": has_default = True if has_default: default_code += case_code if has_break: break return default_code, all_cases_have_break default_code, all_cases_have_break = process_cases(cases) # using 2 temp cells: need_to_execute, expression_value # need_to_execute - initialized with 1, zeroed if running any case. indicating we should execute code for one of the cases # expression_value - initialized with expression's value, this is what we compare our cases' values to code = "[-]+" # need_to_execute = 1 code += ">" # point to next cell code += expression_code # evaluate expression code += "<" # point to expression if all_cases_have_break: # small optimization for evaluating the expression cases = [ case for case in cases if case[0] != "default" ] # remove default to be able to sort. it is handled differently cases.sort(key=lambda x: x[0], reverse=True) # Can sort since correct flow is not needed """ This loop compares the expression value to each case in the switch-case statement, in reverse order It does so by increasing and decreasing expression, and comparing result to 0 E.G. if we have switch(x) { case 2: case 0: case 5: case 1: } x will be put in cell, then: Iteration 1 will "increase" cell by -1 (0-1) (comparing x with 1) Iteration 2 will "increase" cell by -4 (1-5) (comparing x with 5) Iteration 3 will increase cell by +5 (5-0) (comparing x with 0) Iteration 4 will "increase" cell by -2 (0-2) (comparing x with 2) """ # at this point, we point to expression_value cell comparisons = 0 last_case_val = 0 for case, _, _ in reversed(cases): if case == "default": continue # default is handled differently code += get_set_cell_value_code(-case, last_case_val) last_case_val = -case code += "[" # "if zero then jump to matching code part" comparisons += 1 """ Then we add each case's code in the correct order: [ [ [ [ ] ] ] ] notice each case uses the next case's ']' instruction to return to the comparisons block for example, the '[' in case 5 line uses the ']' of case 1 code to "return" to the comparisons this is because there is no way to "skip" code """ # This code will execute after all the comparisons are done and non of the cases executed if default_code: code += ">" # point to next available cell for running the "default" code code += default_code # add code for default case (it also includes all the following cases until break) code += "<" # point to expression_value code += "<-" # need_to_execute = 0 code += ">[-]" # expression_value = 0. When going back to last comparison, it will be 0, so we skip the default if comparisons > 0: code += "]" # "jump back address" of the last comparison comparisons -= 1 # Add all the cases code for case_index, (case, case_code, has_break) in enumerate(cases): if case == "default": continue # default is handled differently if has_break or case_code or default_code: # Meaning this case is not identical to the following case # Or there exist a default case. And because it is handled differently, we need to have its code multiple times in different locations # (if they are identical then no need to generate the same code multiple times (one for each case). # this case will use the following case's code in the next loop iteration) # Generate code for this case (unique) code += "<" # point to need_to_execute code += "[" # if its non-zero (i.e need to execute the code for this case) code += ">>" # point to next available cell for running the code # Insert the code from this case and all the following cases until reaching break # This generates a lot of code since each case includes all following cases until reaching break for _, following_case_code, following_has_break in cases[case_index:]: code += following_case_code if following_has_break: break code += "<<" # point to need_to_execute code += "-" # need_to_execute=0 code += "]" # # end if code += ">" # point to expression_value if comparisons > 0: code += "]" # "jump back address" of the comparison before us comparisons -= 1 # end of the switch-case code += "<" # point to need_to_execute, which becomes next available cell return code def get_copy_from_variable_code(ids_map_list, ID_token, current_pointer): # returns code that copies the value from cell of variable ID to current pointer, and then sets the pointer to the next cell offset = get_offset_to_variable(ids_map_list, ID_token, current_pointer) code = "[-]" # res = 0 code += ">[-]" # temp (next cell) = 0 code += "<" * (offset + 1) # point to destination cell code += "[" + ">" * offset + "+>+" + "<" * (offset + 1) + "-]" # increase res and temp, zero destination code += ">" * (offset + 1) # point to temp code += "[" + "<" * (offset + 1) + "+" + ">" * (offset + 1) + "-]" # copy temp back to destination # at this point we point to the next available cell, which is temp, which is now zero return code def get_token_ID_code(ids_map_list, token, current_pointer): # generate code that evaluates the ID token at the current pointer, and sets the pointer to point to the next available cell return get_copy_from_variable_code(ids_map_list, token, current_pointer) def get_literal_token_code(token): # generate code that evaluates the token at the current pointer, and sets the pointer to point to the next available cell assert is_token_literal(token) if token.type == Token.TRUE: code = "[-]" # zero current cell code += "+" # current cell = 1 code += ">" # point to next cell return code elif token.type == Token.FALSE: code = "[-]" # zero current cell code += ">" # point to next cell return code else: value = get_literal_token_value(token) code = "[-]" # zero current cell code += get_set_cell_value_code(value, 0) # set current cell to the value code += ">" # point to the next cell return code def get_divmod_code(right_token=None): # given that the current pointer points to a, and the cell after a contains b, # (i.e the cells look like: --> a, b, ?, ?, ?, ?, ...) # returns a code that calculates divmod, and the cells look like this: # --> 0, b-a%b, a%b, a/b, 0, 0 # and the pointer points to the first 0 (which is in the same cell as a used to be) ADD_DIVISION_BY_ZERO_CHECK = True if right_token is not None and right_token.type == Token.NUM: if get_NUM_token_value(right_token) == 0: raise BFSemanticError("Dividing by Zero, at %s" % right_token) ADD_DIVISION_BY_ZERO_CHECK = False def get_if_equal_to_0_code(inside_if_code, offset_to_temp_cell): """ given a , wraps it with an "if (current_cell == 0) {}" in the process, it zeros the current cell additionally, it uses a temp cell the argument is the offset from the current cell to the temp cell *** note that the temp cell must be AFTER the cells that the touches *** should assume it starts running when pointing to the current cell and it should end its run pointing to the same cell """ # temp cell is initialized to 1, and holds a flag of whether or not we should run or not # if cell to evaluate is not zero, we set this flag to 0 code = ">" * offset_to_temp_cell # point to temp code += "[-]+" # temp = 1 code += "<" * offset_to_temp_cell # point to cell to compare to 0 code += "[" # if it is not zero code += ">" * offset_to_temp_cell # point to temp code += "-" # temp = 0 code += "<" * offset_to_temp_cell # point to cell code += "[-]" # zero the cell code += "]" # end if code += ">" * offset_to_temp_cell # point to temp cell code += "[" # if it is non zero code += "<" * offset_to_temp_cell # point to cell code += inside_if_code # execute desired code # at this point we point to the original cell code += ">" * offset_to_temp_cell # point to temp cell code += "-" # temp = 0 code += "]" # end if code += "<" * offset_to_temp_cell # point back to original cell return code code = "" if ADD_DIVISION_BY_ZERO_CHECK: # create a prefix code: if (b == 0) {print("Error - Division by zero\n");} # copy b to temp cell (via another temp cell) and compare that cell to 0. if its 0, execute error print and go to infinite loop code += ">>" # point to empty cell code += "[-]>[-]" # zero 2 temp cells code += "<<" # point to b code += "[>+>+<<-]" # move b to both cells code += ">" # point to first cell code += "[<+>-]" # move first cell back to b code += ">" # point to second cell code_inside_if = get_print_string_code("Error - Division by zero\n") code_inside_if += "[]" # infinite loop code += get_if_equal_to_0_code(code_inside_if, offset_to_temp_cell=1) code += "<<<" # point to a # ======================= end of prefix ======================= # a, b, w, x, y, z code += ">>[-]>[-]>[-]>[-]<<<<<" # zero w,x,y,z, and point to a code += "[" # while a != 0 code += "-" # decrease a by 1 code += ">-" # decrease b by 1 code += ">+" # increase w by 1 code += "<" # point to b code += "[->>>+>+<<<<]>>>>[-<<<<+>>>>]" # copy b to y (via z) code += "<" # point to y code_inside_if = "" code_inside_if += "<+" # increase x by 1 code_inside_if += "<" # point to w code_inside_if += "[-<+>]" # copy w to b (b is already 0) (after this we point to w) code_inside_if += ">>" # point to y # get_if_equal_to_0 also zeros y # i set offset_to_temp_cell = 1 because it can use z, since it is unused inside the if code += get_if_equal_to_0_code(inside_if_code=code_inside_if, offset_to_temp_cell=1) code += "<<<<" # point to a code += "]" # end while """ a, b, w, x, y, z w, x, y, z = 0, 0, 0, 0 while a != 0 a -= 1 b -= 1 w += 1 if b == 0: (this means that w = original b) (implementation: copy b to y (via z) and compare y to 0, (then zero y)) x += 1 b = w w = 0 at the end: w = a%b x = a/b b = b-a%b """ return code def get_bitwise_code(code_logic): # a, b, c, w, x, y, z, bit1, bitcounter, res # code_logic uses the cells y, z, and bit1. Where y is res and z and bit1 are the bits. # y is zero. z and bit1 should be zero after code_logic. code = ">" * 7 # point to bit1 code += "[-]" # zero bit1 code += ">" # point to bitcounter code += ">[-]<" # zero res code += "[-]--------[++++++++" # while bitcounter != 8: code += "<" code += "<[-]" * 5 # clear c, w, x, y, z code += "++" # c = 2 code += "<<" # point to a code += "[" # while a != 0: code += "-" # a -= 1 code += ">>-" # c -= 1 code += "[>+>>+<<<-]>[<+>-]" # copy c to y (using w) code += ">>" # point to y code += ">>+<<" # bit1 += 1 code += "-[" # if y != 1: code += "<+" # x += 1 code += "<<++" # c += 2 (c was 0) code += ">" * 5 # point to bit1 code += "--" # bit1 -= 2 (bit1 was 2) code += "<<" # point to y code += "+" # set y to 0 code += "]" # end if code += "<<<<<" # point to a code += "]" # end while code += ">>>>[<<<<+>>>>-]" # move x to a (x is a/2) code += "<<[-]++" # c = 2 code += "<" # point to b code += "[" # while b != 0: code += "-" # b -= 1 code += ">-" # c -= 1 code += "[>+>>+<<<-]>[<+>-]" # copy c to y (using w) code += ">>" # point to y code += ">+<" # z += 1 code += "-[" # if y != 1: code += ">--<" # z -= 2 (z was 2) code += "<+" # x += 1 code += "<<++" # c += 2 (c was 0) code += ">>>" # point to y code += "+" # set y to 0 code += "]" code += "<<<<" # point to b code += "]" # end while # w is a % 2 # x is a / 2 code += ">>>[<<<+>>>-]" # move x to b code += ">>" # point to z code += code_logic # pointer ends at bit1, z and bit1 should be 0 after code code += ">[<+<+>>-]<[>+<-]" # copy bit to z (using bit1) # y = y << z code += "<" code += "[" # while z != 0: code += "<" # point to y code += "[<+>-]" # copy y to x code += "<[>++<-]" # copy x to y * 2 code += ">>-" # z -= 1 code += "]" code += "<" # point to y code += "[>>>>+<<<<-]" # res += y code += ">>>" # point to bitcounter code += "-" * 7 # loop if bitcounter != 7 code += "]" # end while code += ">[<<<<<<<<<+>>>>>>>>>-]" # move res to a code += "<<<<<<<<" # point to b return code def get_unary_prefix_op_code(token, offset_to_variable=None): # returns code that: # performs op on an operand that is at the current pointer # the result is placed in the cell of the operand # and the pointer points to the cell right after it (which becomes the next available cell) if token.type == Token.NOT: # a temp code = ">" # point to temp code += "[-]+" # temp = 1 code += "<" # point to a code += "[" # if a is non-zero code += ">-" # temp = 0 code += "<[-]" # zero a code += "]" # end if code += ">" # point to temp code += "[" # if temp is non-zero code += "<+" # a = 1 code += ">-" # temp = 0 code += "]" # end if return code elif token.type == Token.INCREMENT: # returns code that copies the value from the variable's cell at the given offset, and adds 1 to both the copied and the original cell assert offset_to_variable is not None offset = offset_to_variable code = "[-]" # res = 0 code += ">[-]" # temp (next pointer) = 0 code += "<" * (offset + 1) # point to destination cell code += "+" # increase destination by 1 code += "[" + ">" * offset + "+>+" + "<" * (offset + 1) + "-]" # increase res and temp, zero destination code += ">" * (offset + 1) # point to temp code += "[" + "<" * (offset + 1) + "+" + ">" * (offset + 1) + "-]" # copy temp back to destination # at this point we point to the next available cell, which is temp, which is now zero return code elif token.type == Token.DECREMENT: # returns code that copies the value from the variable's cell at the given offset, and subtracts 1 from both the copied and the original cell assert offset_to_variable is not None offset = offset_to_variable code = "[-]" # res = 0 code += ">[-]" # temp (next pointer) = 0 code += "<" * (offset + 1) # point to destination cell code += "-" # decrease destination by 1 code += "[" + ">" * offset + "+>+" + "<" * (offset + 1) + "-]" # increase res and temp, zero destination code += ">" * (offset + 1) # point to temp code += "[" + "<" * (offset + 1) + "+" + ">" * (offset + 1) + "-]" # copy temp back to destination # at this point we point to the next available cell, which is temp, which is now zero return code elif token.type == Token.UNARY_MULTIPLICATIVE: # returns code that copies the value from the variable's cell at the given offset, modifies both the copied and the original cell depending on the op assert offset_to_variable is not None offset = offset_to_variable if token.data in ["**", "//"]: code = "[-]" # res = 0 code += ">[-]" # temp (next pointer) = 0 code += "<" * (offset + 1) # point to destination cell code += "[" + ">" * offset + "+>+" + "<" * (offset + 1) + "-]" # increase res and temp, zero destination code += ">" * offset # point to res code += ">" # point to temp (**x, //x keep x the same) code += "[" + "<" * (offset + 1) + "+" + ">" * (offset + 1) + "-]" # copy temp back to destination # at this point we point to the next available cell return code elif token.data == "%%": code = "[-]" # res = 0 code += "<" * offset # point to destination cell code += "[-]" # zero destination code += ">" * offset # point to res code += ">" # point the next available cell # at this point we point to the next available cell return code else: raise BFSyntaxError("Unexpected unary prefix %s" % str(token)) elif token.type == Token.BITWISE_NOT: # a temp code = "[>+<-]" # move a into temp code += ">" # point to temp code += "+[<->-]" # invert temp into a return code elif token.type == Token.BINOP: assert token.data in ["+", "-"] if token.data == "+": # keep value as-is return ">" elif token.data == "-": # a temp code = ">[-]" # zero temp code += "<" # point to a code += "[->-<]" # sub a from temp code += ">" # point to temp code += "[<+>-]" # copy temp to a return code raise NotImplementedError def get_unary_postfix_op_code(token, offset_to_variable): # returns code that: # performs op on operand that is at the current pointer # the result is placed in the cell of the operand # and the pointer points to the cell right after it (which becomes the next available cell) if token.type == Token.INCREMENT: # returns code that copies the value from the variable's cell at the given offset, and adds 1 to the original cell offset = offset_to_variable code = "[-]" # res = 0 code += ">[-]" # temp (next pointer) = 0 code += "<" * (offset + 1) # point to destination cell code += "[" + ">" * offset + "+>+" + "<" * (offset + 1) + "-]" # increase res and temp, zero destination code += ">" * (offset + 1) # point to temp code += "+" # increase temp by 1 code += "[" + "<" * (offset + 1) + "+" + ">" * (offset + 1) + "-]" # copy temp back to destination # at this point we point to the next available cell, which is temp, which is now zero return code elif token.type == Token.DECREMENT: # returns code that copies the value from the variable's cell at the given offset, and subtracts 1 from the original cell offset = offset_to_variable code = "[-]" # res = 0 code += ">[-]" # temp (next pointer) = 0 code += "<" * (offset + 1) # point to destination cell code += "[" + ">" * offset + "+>+" + "<" * (offset + 1) + "-]" # increase res and temp, zero destination code += ">" * (offset + 1) # point to temp code += "-" # decrease temp by 1 code += "[" + "<" * (offset + 1) + "+" + ">" * (offset + 1) + "-]" # copy temp back to destination # at this point we point to the next available cell, which is temp, which is now zero return code elif token.type == Token.UNARY_MULTIPLICATIVE: # returns code that copies the value from the variable's cell at the given offset, and modifies the original cell depending on the operation offset = offset_to_variable code = "[-]" # res = 0 code += ">[-]" # temp (next pointer) = 0 code += "<" * (offset + 1) # point to destination cell code += "[" + ">" * offset + "+>+" + "<" * (offset + 1) + "-]" # increase res and temp, zero destination code += ">" * (offset + 1) # point to temp if token.data in ["**", "//"]: pass # x**, x// keeps x the same elif token.data == "%%": # at this point we zeroed x and we point to temp (next available cell) return code # no need to copy anything back to destination - x%% modifies x to 0 else: raise BFSyntaxError("Unexpected unary postfix %s" % str(token)) code += "[" + "<" * (offset + 1) + "+" + ">" * (offset + 1) + "-]" # copy temp back to destination # at this point we point to the next available cell, which is temp, which is now zero return code raise NotImplementedError def get_op_between_literals_code(op_token, right_token=None): # returns code that: # performs op on 2 operands # the first operand is at current pointer, and the second operand is at current pointer + 1 # the code can destroy second operand, and everything after it # the result is placed in the cell of the first operand # and the pointer points to the cell right after it (which becomes the next available cell) op = op_token.data if op == "+" or op == "-": code = ">[<" + op + ">-]" # increase/decrease the first operand and decrease the second operand # the pointer now points to the next available cell, which is the second operand, which is 0 return code elif op == "*": # a, b, temp1, temp2 code = ">>[-]" # temp1 = 0 code += ">[-]" # temp2 = 0 code += "<<<" # point to first operand code += "[>>>+<<<-]" # move first operand to temp2 code += ">>>" # point to temp2 # do in a loop: as long as temp2 != 0 code += "[" code += "<<" # point to second operand code += "[<+>>+<-]" # add it to first operand and temp1 code += ">" # point to temp1 code += "[<+>-]" # move it to second operand # end loop code += ">" # point back to temp2 code += "-" # decrease temp2 code += "]" code += "<<" # point back to next available cell (second operand) return code elif op == "/": code = get_divmod_code(right_token) code += ">>>" # point to a/b code += "[<<<+>>>-]" # copy a/b to current cell code += "<<" # point to next available cell return code elif op == "%": code = get_divmod_code(right_token) code += ">>" # point to a%b code += "[<<+>>-]" # copy a%b to current cell code += "<" # point to next available cell return code # relops elif op == "==": # a, b code = "[->-<]" # a = 0, b = b - a code += "+" # a = 1. will hold the result. if a!=b, this is unchanged code += ">" # point to b code += "[" # if b == 0, enter the following code code += "<->[-]" # a = 0, b=0 code += "]" # end of "loop" return code elif op == "!=": # a, b code = "[->-<]" # a = 0, b = b - a # a will hold the result. if a != b, this is unchanged code += ">" # point to b code += "[" # if b == 0, enter the following code code += "<+>[-]" # a = 1, b=0 code += "]" # end of "loop" return code elif op == ">": # a, b, c, d code = ">>[-]" # c = 0 (will hold res) code += ">[-]" # d = 0 code += "<<<" # point to a code += "[" # while a != 0 code += ">>[-]" # c = 0 code += "<" # point to b code += "[>+>+<<-]>[<+>-]" # copy b to d (via c) code += "+" # c = 1 (will hold res) code += ">" # point to d code += "[" # if d != 0 code += "[-]" # d = 0 code += "<-" # c = 0 code += "<-" # b -= 1 code += ">>" # point to d code += "]" # end if code += "<<<" # point to a code += "-" # a -= 1 code += "]" # end while # move c to a code += ">>" # point to c code += "[<<+>>-]" # move c to a code += "<" # point to b (next available cell) """ x > y? res = 0 while x != 0: res = 1 if y != 0: res = 0 y -= 1 x -= 1 """ return code elif op == "<": # similar to > # a, b, c, d code = ">>[-]" # c = 0 (will hold res) code += ">[-]" # d = 0 code += "<<" # point to b code += "[" # while b != 0 code += ">[-]" # c = 0 code += "<<" # point to a code += "[>>+>+<<<-]>>[<<+>>-]" # copy a to d (via c) code += "+" # c = 1 (will hold res) code += ">" # point to d code += "[" # if d != 0 code += "[-]" # d = 0 code += "<-" # c = 0 code += "<<-" # a -= 1 code += ">>>" # point to d code += "]" # end if code += "<<" # point to b code += "-" # b -= 1 code += "]" # end while # move c to a code += "<" # point to a code += "[-]" # a = 0 code += ">>" # point to c code += "[<<+>>-]" # move c to a code += "<" # point to b (next available cell) """ x < y? res = 0 while y != 0: res = 1 if x != 0: res = 0 x -= 1 y -= 1 """ return code elif op == "<=": # a, b, c, d code = ">>[-]+" # c = 1 (will hold res) code += ">[-]" # d = 0 code += "<<<" # point to a code += "[" # while a != 0 code += ">>[-]" # c = 0 code += "<" # point to b code += "[>+>+<<-]>[<+>-]" # copy b to d (via c) code += ">" # point to d code += "[" # if d != 0 code += "[-]" # d = 0 code += "<+" # c = 1 code += "<-" # b -= 1 code += ">>" # point to d code += "]" # end if code += "<<<" # point to a code += "-" # a -= 1 code += "]" # end while # move c to a code += ">>" # point to c code += "[<<+>>-]" # move c to a code += "<" # point to b (next available cell) """ x <= y? res = 1 while x != 0: res = 0 if y != 0: res = 1 y -= 1 x -= 1 """ return code elif op == ">=": # similar to <= # a, b, c, d code = ">>[-]+" # c = 1 (will hold res) code += ">[-]" # d = 0 code += "<<" # point to b code += "[" # while b != 0 code += ">[-]" # c = 0 code += "<<" # point to a code += "[>>+>+<<<-]>>[<<+>>-]" # copy a to d (via c) code += ">" # point to d code += "[" # if d != 0 code += "[-]" # d = 0 code += "<+" # c = 1 code += "<<-" # a -= 1 code += ">>>" # point to d code += "]" # end if code += "<<" # point to b code += "-" # b -= 1 code += "]" # end while # move c to a code += "<" # point to a code += "[-]" # a = 0 code += ">>" # point to c code += "[<<+>>-]" # move c to a code += "<" # point to b (next available cell) """ x >= y? res = 1 while y != 0: res = 0 if x != 0: res = 1 x -= 1 y -= 1 """ return code elif op == "<<": # a, b, temp code = ">>[-]" # zero temp code += "<" # point to b code += "[" # while b != 0 code += "<" # point to a code += "[>>+<<-]" # copy a to temp code += ">>" # point to temp code += "[<<++>>-]" # multiply temp by 2 and store result in a code += "<-" # point to b and b -= 1 code += "]" # end while return code elif op == ">>": # a, b, c, x, y, z code = ">" # point to b code += ">[-]" * 4 # clear 4 cells code += "<" * 4 # point to b code += "[" # while b != 0 code += ">++" # set c to 2 code += "<<" # point to a code += "[" # while a != 0 code += "-" # a -= 1 code += ">>-" # c -= 1 code += "[>>+>+<<<-]>>>[<<<+>>>-]" # copy c to y (via z) code += "<" # point to y code += "-[" # if y == 0 code += "<+" # x += 1 code += "<++" # set c to 2 code += ">>" code += "+" # zero y code += "]" # end if code += "<<<<" # point to a code += "]" # end while code += ">>>" # point to x code += "[<<<+>>>-]" # move x to a code += "<[-]" # zero c code += "<-" # b -= 1 code += "]" # end while return code elif op_token.type == Token.BITWISE_AND: code = get_bitwise_code("[->[-<<+>>]<]>[-]") return code elif op_token.type == Token.BITWISE_OR: code = get_bitwise_code("[>+<-]>[[-]<<+>>]") return code elif op_token.type == Token.BITWISE_XOR: code = get_bitwise_code("[>-<-]>[[-]<<+>>]") return code raise NotImplementedError def get_op_boolean_operator_code(node, current_pointer): # short-circuit evaluation of AND and OR assert node.token.type in [Token.AND, Token.OR] if node.token.type == Token.AND: # result, operand code = "[-]" # zero result code += ">" # point to next cell code += node.left.get_code(current_pointer + 1) # evaluate first operand code += "<" # point to first operand code += "[" # if it is non-zero code += "[-]" # zero first operand code += node.right.get_code(current_pointer + 1) # evaluate second operand code += "<" # point to second operand code += "[" # if it is non-zero code += "<+>" # result = 1 code += "[-]" # zero second operand code += "]" # end if code += "]" # end if # now we point to one after result (next available cell) return code elif node.token.type == Token.OR: # result, check_second_operand/second_operand, first_operand code = "[-]" # zero result code += ">" # point to check_second_operand code += "[-]+" # check_second_operand = 1 code += ">" # point to next cell code += node.left.get_code(current_pointer + 2) # evaluate first operand code += "<" # point to first operand code += "[" # if it is non-zero code += "<<+" # result = 1 code += ">-" # check_second_operand = 0 code += ">[-]" # zero first operand code += "]" # end if code += "<" # point to check_second_operand code += "[" # if check_second_operand code += node.right.get_code(current_pointer + 1) # evaluate second operand code += "<" # point to second operand code += "[" # if it is non-zero code += "<+>" # result = 1 code += "[-]" # zero second operand code += "]" # end if code += "]" # end if # now we point to one after result (next available cell) return code raise NotImplementedError def get_print_string_code(string): code = "[-]" # zero the current cell code += ">[-]" # zero the next cell (will be used for loop counts) code += "<" # point to the original cell ("character" cell) prev_value = 0 for i in range(len(string)): current_value = ord(string[i]) code += get_set_cell_value_code(current_value, prev_value, zero_next_cell_if_necessary=False) code += "." prev_value = current_value return code def get_move_right_index_cells_code(current_pointer, node_index): # used for arrays # returns a code that evaluates the index, then moves the pointer right, amount of cells # at the end of execution, the layout is: # 0 index next_available_cell (point to next available cell) # index, steps_taken_counter code = node_index.get_code(current_pointer) # index code += "[-]" # counter = 0 code += "<" # point to index code += "[" # while index != 0 code += ">>" # point to new_counter (one after current counter) code += "[-]+" # zero new_counter then add 1 to the new_counter code += "<" # move to old counter code += "[>+<-]" # add old counter to new counter code += "<" # point to old index code += "-" # sub 1 from old index code += "[>+<-]" # move old index to new index code += ">" # point to new index code += "]" # end while # old_index=0 new_index res (pointing to old index) code += ">>" # point to res return code def get_move_left_index_cell_code(): # used for arrays # complement of "get_move_right_index_cells_code" # assumes the layout is: # value, index (pointing to index) # moves cells left, and moving along with it # in the end, point to one cell after (which becomes the next available cell) # layout: res, index (pointing to index) code = "[" # while new_index != 0 code += "<" # point to res code += "[<+>-]" # move res to the left code += ">" # point to new_index code += "-" # sub 1 from index code += "[<+>-]" # move new_index to left code += "<" # point to new index code += "]" # end while # now res is at the desired cell, and we point to the next available cell return code # ================= # General # ================= def get_literal_token_value(token): # known at compilation time assert is_token_literal(token) if token.type == Token.NUM: return get_NUM_token_value(token) elif token.type == Token.TRUE: return 1 elif token.type == Token.FALSE: return 0 elif token.type == Token.CHAR: return ord(token.data) def get_NUM_token_value(token): if token.data.startswith("0x"): return int(token.data, 16) elif token.data.startswith("0o"): return int(token.data, 8) elif token.data.startswith("0b"): return int(token.data, 2) else: return int(token.data) def get_variable_from_ID_token(ids_map_list, ID_token): ID = ID_token.data # given an id, goes through the ids map list and returns the index of the first ID it finds for i in range(len(ids_map_list)): ids_map = ids_map_list[i].IDs_dict if ID in ids_map: return ids_map[ID] raise BFSemanticError("'%s' does not exist" % str(ID_token)) def dimensions_to_size(dimensions): return reduce(lambda x, y: x * y, dimensions) def get_variable_dimensions_from_token(ids_map_list, ID_token): variable = get_variable_from_ID_token(ids_map_list, ID_token) return variable.dimensions def get_id_index(ids_map_list, ID_token): variable = get_variable_from_ID_token(ids_map_list, ID_token) return variable.cell_index def get_offset_to_variable(ids_map_list, ID_token, current_pointer): offset = current_pointer - get_id_index(ids_map_list, ID_token) return offset def is_token_literal(token): # token with value that is known at compilation time return token.type in [Token.TRUE, Token.FALSE, Token.NUM, Token.CHAR]