1 # Structured control flow using break/loop rather than jump. 2 # 3 # To run (on Linux): 4 # $ ./translate_subx init.linux [012]*.subx subx-params.subx braces.subx 5 # $ mv a.elf braces 6 # 7 # Example 1: 8 # $ cat x.subx 9 # { 10 # 7c/jump-if-< break/disp8 11 # 74/jump-if-= loop/disp8 12 # } 13 # $ cat x.subx |braces 14 # _loop1: 15 # 7c/jump-if-< _break1/disp8 16 # 74/jump-if-= _loop1/disp8 17 # _break1: 18 # 19 # Example 2: 20 # $ cat x.subx 21 # { 22 # 7c/jump-if-< break/disp8 23 # } 24 # { 25 # 74/jump-if-= loop/disp8 26 # } 27 # $ cat x.subx |braces 28 # _loop1: 29 # 7c/jump-if-< _break1/disp8 30 # _break1: 31 # _loop2: 32 # 74/jump-if-= _loop2/disp8 33 # _break2: 34 # 35 # Example 3: 36 # $ cat x.subx 37 # { 38 # { 39 # 74/jump-if-= loop/disp8 40 # } 41 # 7c/jump-if-< loop/disp8 42 # } 43 # $ cat x.subx |braces 44 # _loop1: 45 # _loop2: 46 # 74/jump-if-= _loop2/disp8 47 # _break2: 48 # 7c/jump-if-< _loop1/disp8 49 # _break1: 50 51 == code 52 53 Entry: # run tests if necessary, a REPL if not 54 # . prologue 55 89/<- %ebp 4/r32/esp 56 # initialize heap 57 (new-segment *Heap-size Heap) 58 # if (argc <= 1) goto interactive 59 81 7/subop/compare *ebp 1/imm32 60 7e/jump-if-<= $subx-braces-main:interactive/disp8 61 # if (argv[1] != "test")) goto interactive 62 (kernel-string-equal? *(ebp+8) "test") # => eax 63 3d/compare-eax-and 0/imm32/false 64 74/jump-if-= $subx-braces-main:interactive/disp8 65 # 66 (run-tests) 67 # syscall(exit, *Num-test-failures) 68 8b/-> *Num-test-failures 3/r32/ebx 69 eb/jump $subx-braces-main:end/disp8 70 $subx-braces-main:interactive: 71 (subx-braces Stdin Stdout) 72 # syscall(exit, 0) 73 bb/copy-to-ebx 0/imm32 74 $subx-braces-main:end: 75 e8/call syscall_exit/disp32 76 77 subx-braces: # in: (addr buffered-file), out: (addr buffered-file) 78 # pseudocode: 79 # var line: (stream byte 512) 80 # var label-stack: (stack int 32) # at most 32 levels of nesting 81 # var next-label-id: int = 1 82 # while true 83 # clear-stream(line) 84 # read-line-buffered(in, line) 85 # if (line->write == 0) break # end of file 86 # skip-chars-matching-whitespace(line) 87 # if line->data[line->read] == '{' 88 # print(out, "_loop" next-label-id ":\n&qu:(before "End Globals") // Arithmetic ops. const int ADD = 2; :(before "End Primitive Recipe Numbers") Recipe_number["add"] = ADD; assert(Next_recipe_number == ADD); Next_recipe_number++; :(before "End Primitive Recipe Implementations") case ADD: { trace("run") << "ingredient 0 is " << instructions[pc].ingredients[0].name; vector<int> arg0 = read_memory(instructions[pc].ingredients[0]); assert(arg0.size() == 1); trace("run") << "ingredient 1 is " << instructions[pc].ingredients[1].name; vector<int> arg1 = read_memory(instructions[pc].ingredients[1]); assert(arg1.size() == 1); vector<int> result; result.push_back(arg0[0] + arg1[0]); trace("run") << "product 0 is " << result[0]; write_memory(instructions[pc].products[0], result); break; } :(scenario "add_literal") recipe main [ 1:integer <- add 23:literal, 34:literal ] +run: instruction main/0 +run: ingredient 0 is 23 +run: ingredient 1 is 34 +run: product 0 is 57 +mem: storing 57 in location 1 :(scenario "add") recipe main [ 1:integer <- copy 23:literal 2:integer <- copy 34:literal 3:integer <- add 1:integer, 2:integer ] +run: instruction main/2 +run: ingredient 0 is 1 +mem: location 1 is 23 +run: ingredient 1 is 2 +mem: location 2 is 34 +run: product 0 is 57 +mem: storing 57 in location 3 :(before "End Globals") const int SUBTRACT = 3; :(before "End Primitive Recipe Numbers") Recipe_number["subtract"] = SUBTRACT; assert(Next_recipe_number == SUBTRACT); Next_recipe_number++; :(before "End Primitive Recipe Implementations") case SUBTRACT: { trace("run") << "ingredient 0 is " << instructions[pc].ingredients[0].name; vector<int> arg0 = read_memory(instructions[pc].ingredients[0]); assert(arg0.size() == 1); trace("run") << "ingredient 1 is " << instructions[pc].ingredients[1].name; vector<int> arg1 = read_memory(instructions[pc].ingredients[1]); assert(arg1.size() == 1); vector<int> result; result.push_back(arg0[0] - arg1[0]); trace("run") << "product 0 is " << result[0]; write_memory(instructions[pc].products[0], result); break; } :(scenario "subtract_literal") recipe main [ 1:integer <- subtract 5:literal, 2:literal ] +run: instruction main/0 +run: ingredient 0 is 5 +run: ingredient 1 is 2 +run: product 0 is 3 +mem: storing 3 in location 1 :(scenario "subtract") recipe main [ 1:integer <- copy 23:literal 2:integer <- copy 34:literal 3:integer <- subtract 1:integer, 2:integer ] +run: instruction main/2 +run: ingredient 0 is 1 +mem: location 1 is 23 +run: ingredient 1 is 2 +mem: location 2 is 34 +run: product 0 is -11 +mem: storing -11 in location 3 :(before "End Globals") const int MULTIPLY = 4; :(before "End Primitive Recipe Numbers") Recipe_number["multiply"] = MULTIPLY; assert(Next_recipe_number == MULTIPLY); Next_recipe_number++; :(before "End Primitive Recipe Implementations") case MULTIPLY: { trace("run") << "ingredient 0 is " << instructions[pc].ingredients[0].name; vector<int> arg0 = read_memory(instructions[pc].ingredients[0]); assert(arg0.size() == 1); trace("run") << "ingredient 1 is " << instructions[pc].ingredients[1].name; vector<int> arg1 = read_memory(instructions[pc].ingredients[1]); assert(arg1.size() == 1); trace("run") << "ingredient 1 is " << arg1[0]; vector<int> result; result.push_back(arg0[0] * arg1[0]); trace("run") << "product 0 is " << result[0]; write_memory(instructions[pc].products[0], result); break; } :(scenario "multiply_literal") recipe main [ 1:integer <- multiply 2:literal, 3:literal ] +run: instruction main/0 +run: ingredient 0 is 2 +run: ingredient 1 is 3 +run: product 0 is 6 +mem: storing 6 in location 1 :(scenario "multiply") recipe main [ 1:integer <- copy 4:literal 2:integer <- copy 6:literal 3:integer <- multiply 1:integer, 2:integer ] +run: instruction main/2 +run: ingredient 0 is 1 +mem: location 1 is 4 +run: ingredient 1 is 2 +mem: location 2 is 6 +run: product 0 is 24 +mem: storing 24 in location 3 :(before "End Globals") const int DIVIDE = 5; :(before "End Primitive Recipe Numbers") Recipe_number["divide"] = DIVIDE; assert(Next_recipe_number == DIVIDE); Next_recipe_number++; :(before "End Primitive Recipe Implementations") case DIVIDE: { trace("run") << "ingredient 0 is " << instructions[pc].ingredients[0].name; vector<int> arg0 = read_memory(instructions[pc].ingredients[0]); assert(arg0.size() == 1); trace("run") << "ingredient 1 is " << instructions[pc].ingredients[1].name; vector<int> arg1 = read_memory(instructions[pc].ingredients[1]); assert(arg1.size() == 1); trace("run") << "ingredient 1 is " << arg1[0]; vector<int> result; result.push_back(arg0[0] / arg1[0]); trace("run") << "product 0 is " << result[0]; write_memory(instructions[pc].products[0], result); break; } :(scenario "divide_literal") recipe main [ 1:integer <- divide 8:literal, 2:literal ] +run: instruction main/0 +run: ingredient 0 is 8 +run: ingredient 1 is 2 +run: product 0 is 4 +mem: storing 4 in location 1 :(scenario "divide") recipe main [ 1:integer <- copy 27:literal 2:integer <- copy 3:literal 3:integer <- divide 1:integer, 2:integer ] +run: instruction main/2 +run: ingredient 0 is 1 +mem: location 1 is 27 +run: ingredient 1 is 2 +mem: location 2 is 3 +run: product 0 is 9 +mem: storing 9 in location 3 :(before "End Globals") const int DIVIDE_WITH_REMAINDER = 6; :(before "End Primitive Recipe Numbers") Recipe_number["divide_with_remainder"] = DIVIDE_WITH_REMAINDER; assert(Next_recipe_number == DIVIDE_WITH_REMAINDER); Next_recipe_number++; :(before "End Primitive Recipe Implementations") case DIVIDE_WITH_REMAINDER: { trace("run") << "ingredient 0 is " << instructions[pc].ingredients[0].name; vector<int> arg0 = read_memory(instructions[pc].ingredients[0]); assert(arg0.size() == 1); trace("run") << "ingredient 1 is " << instructions[pc].ingredients[1].name; vector<int> arg1 = read_memory(instructions[pc].ingredients[1]); assert(arg1.size() == 1); vector<int> result0; result0.push_back(arg0[0] / arg1[0]); trace("run") << "product 0 is " << result0[0]; write_memory(instructions[pc].products[0], result0); vector<int> result1; result1.push_back(arg0[0] % arg1[0]); trace("run") << "product 1 is " << result1[0]; write_memory(instructions[pc].products[1], result1); break; } :(scenario "divide_with_remainder_literal") recipe main [ 1:integer, 2:integer <- divide_with_remainder 9:literal, 2:literal ] +run: instruction main/0 +run: ingredient 0 is 9 +run: ingredient 1 is 2 +run: product 0 is 4 +mem: storing 4 in location 1 +run: product 1 is 1 +mem: storing 1 in location 2 :(scenario "divide_with_remainder") recipe main [ 1:integer <- copy 27:literal 2:integer <- copy 11:literal 3:integer, 4:integer <- divide_with_remainder 1:integer, 2:integer ] +run: instruction main/2 +run: ingredient 0 is 1 +mem: location 1 is 27 +run: ingredient 1 is 2 +mem: location 2 is 11 +run: product 0 is 2 +mem: storing 2 in location 3 +run: product 1 is 5 +mem: storing 5 in location 4