1 package legacy.forth;
2
3 import java.util.Arrays;
4 import java.util.HashMap;
5 import java.util.List;
6 import java.util.Map;
7 import java.util.Stack;
8 import java.util.stream.Collectors;
9
10 public class ForthEvaluator {
11 List<Integer> evaluateProgram(List<String> commands) {
12 Stack<Integer> stack = new Stack<Integer>();
13 Map<String, String> name2definition = new HashMap<String, String>();
14 for (String command : commands) {
15 command = command.toLowerCase();
16
17 if (command.startsWith(": ")) {
18 List<String> tokens = tokenize(command.substring(2, command.length() - 2));
19 String name = tokens.get(0);
20
21 if (isNumber(name)) {
22 throw new IllegalArgumentException("Cannot redefine numbers");
23 }
24
25 String definition = replace(String.join(" ", tokens.subList(1, tokens.size())), name2definition);
26 name2definition.put(name, definition);
27 } else {
28 for (String token : tokenize(replace(command, name2definition))) {
29 if (isNumber(token)) {
30 int number = Integer.parseInt(token);
31 stack.push(number);
32 } else if (token.equals("+")) {
33 checkStackSize(stack, "Addition", 2);
34
35 int number1 = stack.pop();
36 int number2 = stack.pop();
37 stack.push(number2 + number1);
38 } else if (token.equals("-")) {
39 checkStackSize(stack, "Subtraction", 2);
40
41 int number1 = stack.pop();
42 int number2 = stack.pop();
43 stack.push(number2 - number1);
44 } else if (token.equals("*")) {
45 checkStackSize(stack, "Multiplication", 2);
46
47 int number1 = stack.pop();
48 int number2 = stack.pop();
49 stack.push(number2 * number1);
50 } else if (token.equals("/")) {
51 checkStackSize(stack, "Division", 2);
52
53 int number1 = stack.pop();
54 int number2 = stack.pop();
55
56 if (number1 == 0) {
57 throw new IllegalArgumentException("Division by 0 is not allowed");
58 }
59
60 stack.push(number2 / number1);
61 } else if (token.equals("dup")) {
62 checkStackSize(stack, "Duplicating", 1);
63
64 stack.push(stack.peek());
65 } else if (token.equals("drop")) {
66 checkStackSize(stack, "Dropping", 1);
67
68 stack.pop();
69 } else if (token.equals("swap")) {
70 checkStackSize(stack, "Swapping", 2);
71
72 int number1 = stack.pop();
73 int number2 = stack.pop();
74 stack.push(number1);
75 stack.push(number2);
76 } else if (token.equals("over")) {
77 checkStackSize(stack, "Overing", 2);
78
79 stack.push(stack.get(stack.size() - 2));
80 } else {
81 throw new IllegalArgumentException(
82 String.format("No definition available for operator \"%s\"", token));
83 }
84 }
85 }
86 }
87 return stack;
88 }
89
90 void checkStackSize(Stack<Integer> stack, String operation, int minSize) {
91 if (stack.size() < minSize) {
92 throw new IllegalArgumentException(String.format("%s requires that the stack contain at least %d value%s",
93 operation, minSize, minSize == 1 ? "" : "s"));
94 }
95 }
96
97 String replace(String command, Map<String, String> name2definition) {
98 return String.join(" ", tokenize(command).stream().map(token -> name2definition.getOrDefault(token, token))
99 .collect(Collectors.toList()));
100 }
101
102 List<String> tokenize(String s) {
103 return Arrays.stream(s.split(" +")).filter(token -> !token.isEmpty()).collect(Collectors.toList());
104 }
105
106 boolean isNumber(String s) {
107 try {
108 Integer.parseInt(s);
109 return true;
110 } catch (NumberFormatException e) {
111 return false;
112 }
113 }
114 }