Commits

Roman Dawydkin committed a48e5a1

More commands, mostly math.

Comments (0)

Files changed (7)

src/toy/CommonCommands.java

 public class CommonCommands
 {
 	public static void installCommands(final Environment env) {
-		new ImmediateCommand(env, "!clr") {
+		new ImmediateCommand(env, "clr") {
 			public void exec(Machine m) {
 				m.reset();
 			}
 				m.push(list.value);
 			}
 		};
-		new Command(env, ".") {
+		new Command(env, "?") {
 			public void exec(Machine m) {
-				final Object obj = m.pop();
-				final String str = (obj == null) ? "[]" : obj.toString();
-				m.printer.printLine(str);
+				final Object obj = m.peek();
+				final StringBuilder buf = new StringBuilder();
+				formatObject(buf, obj);
+				m.printer.printLine(buf.toString());
 			}
 		};
-		new Command(env, "s.") {
+		new Command(env, "s?") {
 			public void exec(Machine m) {
 				final StringBuilder buf = new StringBuilder();
-				append(buf, m.getStack());
+				formatList(buf, m.getStack());
 				m.printer.printLine(buf.toString());
 			}
-			private void append(StringBuilder buf, Cons cons) {
-				boolean tail = false;
-				buf.append("[");
-				while (cons != null) {
-					if (tail) {
-						buf.append(' ');
-					} else {
-						tail = true;
-					}
-					final Object value = cons.value;
-					if (value instanceof Cons) {
-						append(buf, (Cons) value);
-					} else if (value == null) {
-						buf.append("[]");
-					} else {
-						buf.append(value.toString());
-					}
-					cons = cons.next;
-				}
-				buf.append("]");
-			}
 		};
 		new Command(env, "apply") {
 			public void exec(Machine m) {
 		};
 		new Command(env, "size") {
 			public void exec(Machine m) {
-				Cons list = (Cons) m.pop();
-				int result = 0;
-				while (list != null) {
-					result++;
-					list = list.next;
-				}
-				m.push(BigDecimal.valueOf(result));
+				final Cons list = (Cons) m.pop();
+				m.push(BigDecimal.valueOf(Cons.size(list)));
 			}
 		};
 		new Command(env, "reverse") {
 				m.push(Cons.reverse(list));
 			}
 		};
+		new Arg2Command(env, "concat") {
+			protected void exec(Machine m, Object obj1, Object obj2) {
+				m.push(Cons.concat((Cons) obj1, (Cons) obj2));
+			}
+		};
 		// -------------------------------------------------- Control structures
 		new Command(env, "ifte") {
 			/* cond then else --> */
 			}
 		};
 	}
+
+	static void formatList(StringBuilder buf, Cons cons) {
+		boolean tail = false;
+		while (cons != null) {
+			if (tail) {
+				buf.append(' ');
+			} else {
+				tail = true;
+			}
+			formatObject(buf, cons.value);
+			cons = cons.next;
+		}
+	}
+
+	static void formatObject(StringBuilder buf, Object value) {
+		if (value == null) {
+			buf.append("[]");
+		} else if (value instanceof String) {
+			buf.append('"');
+			buf.append(value.toString());
+			buf.append('"');
+		} else if (value instanceof Number) {
+			MathCommands.formatNumber(buf, (Number) value);
+		} else if (value instanceof Cons) {
+			buf.append("[");
+			formatList(buf, (Cons) value);
+			buf.append("]");
+		} else {
+			buf.append(value.toString());
+		}
+	}
 }

src/toy/Cons.java

 		return list.value;
 	}
 
+	static Cons concat(Cons list1, Cons list2) {
+		Cons prefix = reverse(list1);
+		Cons result = list2;
+		while (prefix != null) {
+			result = new Cons(prefix.value, result);
+			prefix = prefix.next;
+		}
+		return result;
+	}
+
+	static int size(Cons list) {
+		int result = 0;
+		while (list != null) {
+			result++;
+			list = list.next;
+		}
+		return result;
+	}
+
 	// ================================================================== Fields
 
 	final Object value;

src/toy/Interpreter.java

 import java.io.IOException;
 import java.io.PushbackReader;
 import java.io.StringReader;
-import java.math.BigDecimal;
 
 public class Interpreter
 {
 		final Command cmd = machine.env.get(token);
 		if (cmd != null) {
 			machine.eval(cmd);
-		} else if (isNumberStart(token)) {
-			final BigDecimal num = new BigDecimal(token);
+			return;
+		}
+		final Number num = MathCommands.parseNumber(token);
+		if (num != null) {
 			machine.push(num);
 		} else {
 			throw new RuntimeException("Invalid token: " + token);
 		}
 	}
-
-	private static boolean isNumberStart(String str) {
-		final char c = str.charAt(0);
-		return Character.isDigit(c) || (c == '.') || (c == '-') || (c == '+');
-	}
 }

src/toy/LogicCommands.java

+package toy;
+
+public class LogicCommands
+{
+	public static void installCommands(Environment env) {
+		new LiteralCommand(env, "true", true);
+		new LiteralCommand(env, "false", false);
+		new Arg2Command(env, "and") {
+			protected void exec(Machine m, Object obj1, Object obj2) {
+				final boolean x = (Boolean) obj1;
+				final boolean y = (Boolean) obj2;
+				m.push(x & y);
+			}
+		};
+		new Arg2Command(env, "or") {
+			protected void exec(Machine m, Object obj1, Object obj2) {
+				final boolean x = (Boolean) obj1;
+				final boolean y = (Boolean) obj2;
+				m.push(x | y);
+			}
+		};
+		new Arg2Command(env, "xor") {
+			protected void exec(Machine m, Object obj1, Object obj2) {
+				final boolean x = (Boolean) obj1;
+				final boolean y = (Boolean) obj2;
+				m.push(y ^ x);
+			}
+		};
+		new Command(env, "not") {
+			public void exec(Machine m) {
+				final boolean x = (Boolean) m.pop();
+				m.push(!x);
+			}
+		};
+	}
+}

src/toy/Machine.java

 
 	static {
 		CommonCommands.installCommands(TOP_ENV);
-		MathCommand.installCommands(TOP_ENV);
+		LogicCommands.installCommands(TOP_ENV);
+		MathCommands.installCommands(TOP_ENV);
 		ComparisonCommand.installCommands(TOP_ENV);
 	}
 

src/toy/MathCommand.java

-package toy;
-
-import java.math.BigDecimal;
-
-public abstract class MathCommand extends Arg2Command
-{
-	public static void installCommands(Environment env) {
-		new MathCommand(env, "+") {
-			protected void exec(Machine m, BigDecimal num1, BigDecimal num2) {
-				m.push(num1.add(num2));
-			}
-		};
-		new MathCommand(env, "-") {
-			protected void exec(Machine m, BigDecimal num1, BigDecimal num2) {
-				m.push(num1.subtract(num2));
-			}
-		};
-		new MathCommand(env, "*") {
-			protected void exec(Machine m, BigDecimal num1, BigDecimal num2) {
-				m.push(num1.multiply(num2));
-			}
-		};
-		new MathCommand(env, "/") {
-			protected void exec(Machine m, BigDecimal num1, BigDecimal num2) {
-				m.push(num1.divide(num2, BigDecimal.ROUND_HALF_UP));
-			}
-		};
-		new MathCommand(env, "%") {
-			protected void exec(Machine m, BigDecimal num1, BigDecimal num2) {
-				m.push(num1.remainder(num2));
-			}
-		};
-		new MathCommand(env, "min") {
-			protected void exec(Machine m, BigDecimal num1, BigDecimal num2) {
-				m.push(num1.min(num2));
-			}
-		};
-		new MathCommand(env, "max") {
-			protected void exec(Machine m, BigDecimal num1, BigDecimal num2) {
-				m.push(num1.max(num2));
-			}
-		};
-		new Command(env, "succ") {
-			public void exec(Machine m) {
-				final BigDecimal num = (BigDecimal) m.pop();
-				m.push(num.add(BigDecimal.ONE));
-			}
-		};
-		new Command(env, "pred") {
-			public void exec(Machine m) {
-				final BigDecimal num = (BigDecimal) m.pop();
-				m.push(num.subtract(BigDecimal.ONE));
-			}
-		};
-		new Command(env, "abs") {
-			public void exec(Machine m) {
-				final BigDecimal num = (BigDecimal) m.pop();
-				m.push(num.abs());
-			}
-		};
-		new Command(env, "sign") {
-			public void exec(Machine m) {
-				final BigDecimal num = (BigDecimal) m.pop();
-				m.push(BigDecimal.valueOf(num.signum()));
-			}
-		};
-		new Command(env, "fact") {
-			public void exec(Machine m) {
-				BigDecimal num = (BigDecimal) m.pop();
-				BigDecimal res = BigDecimal.ONE;
-				while (num.compareTo(BigDecimal.ONE) > 0) {
-					res = res.multiply(num);
-					num = num.subtract(BigDecimal.ONE);
-				}
-				m.push(res);
-			}
-		};
-		new Command(env, "exp") {
-			public void exec(Machine m) {
-				final BigDecimal num = (BigDecimal) m.pop();
-				m.push(BigDecimal.valueOf(Math.exp(num.doubleValue())));
-			}
-		};
-		new Command(env, "log") {
-			public void exec(Machine m) {
-				final BigDecimal num = (BigDecimal) m.pop();
-				m.push(BigDecimal.valueOf(Math.log(num.doubleValue())));
-			}
-		};
-		//	fib == [1 0] dip [swap [+] unary] times popd;
-		//	nfib == [1 1] dip [dup [+ succ] dip swap] times pop;
-		//	gcd == [0 >] [dup rollup rem] while pop;
-		new LiteralCommand(env, "true", true);
-		new LiteralCommand(env, "false", false);
-		new Command(env, "and") {
-			public void exec(Machine m) {
-				final boolean x = (Boolean) m.pop();
-				final boolean y = (Boolean) m.pop();
-				m.push(x & y);
-			}
-		};
-		new Command(env, "or") {
-			public void exec(Machine m) {
-				final boolean x = (Boolean) m.pop();
-				final boolean y = (Boolean) m.pop();
-				m.push(x | y);
-			}
-		};
-		new Command(env, "xor") {
-			public void exec(Machine m) {
-				final boolean x = (Boolean) m.pop();
-				final boolean y = (Boolean) m.pop();
-				m.push(y ^ x);
-			}
-		};
-		new Command(env, "not") {
-			public void exec(Machine m) {
-				final boolean x = (Boolean) m.pop();
-				m.push(!x);
-			}
-		};
-	}
-
-	public MathCommand(Environment env, String name) {
-		super(env, name);
-	}
-
-	protected final void exec(Machine m, Object obj1, Object obj2) {
-		exec(m, (BigDecimal) obj1, (BigDecimal) obj2);
-	}
-
-	protected abstract void exec(Machine m,
-			BigDecimal num1, BigDecimal num2);
-}

src/toy/MathCommands.java

+package toy;
+
+import java.math.BigDecimal;
+
+public class MathCommands
+{
+	public static void installCommands(Environment env) {
+		new Math2Command(env, "+")
+		{
+			protected Object eval(int n1, int n2) {
+				return n1 + n2;
+			}
+			protected Object eval(long n1, long n2) {
+				return n1 + n2;
+			}
+			protected Object eval(float n1, float n2) {
+				return n1 + n2;
+			}
+			protected Object eval(double n1, double n2) {
+				return n1 + n2;
+			}
+			protected Object eval(BigDecimal n1, BigDecimal n2) {
+				return n1.add(n2);
+			}
+		};
+		new Math2Command(env, "-")
+		{
+			protected Object eval(int n1, int n2) {
+				return n1 - n2;
+			}
+			protected Object eval(long n1, long n2) {
+				return n1 - n2;
+			}
+			protected Object eval(float n1, float n2) {
+				return n1 - n2;
+			}
+			protected Object eval(double n1, double n2) {
+				return n1 - n2;
+			}
+			protected Object eval(BigDecimal n1, BigDecimal n2) {
+				return n1.subtract(n2);
+			}
+		};
+		new Math2Command(env, "*")
+		{
+			protected Object eval(int n1, int n2) {
+				return n1 * n2;
+			}
+			protected Object eval(long n1, long n2) {
+				return n1 * n2;
+			}
+			protected Object eval(float n1, float n2) {
+				return n1 * n2;
+			}
+			protected Object eval(double n1, double n2) {
+				return n1 * n2;
+			}
+			protected Object eval(BigDecimal n1, BigDecimal n2) {
+				return n1.multiply(n2);
+			}
+		};
+		new Math2Command(env, "/")
+		{
+			protected Object eval(int n1, int n2) {
+				return n1 / n2;
+			}
+			protected Object eval(long n1, long n2) {
+				return n1 / n2;
+			}
+			protected Object eval(float n1, float n2) {
+				return n1 / n2;
+			}
+			protected Object eval(double n1, double n2) {
+				return n1 / n2;
+			}
+			protected Object eval(BigDecimal n1, BigDecimal n2) {
+				return n1.divide(n2, BigDecimal.ROUND_HALF_UP);
+			}
+		};
+		new Math2Command(env, "%")
+		{
+			protected Object eval(int n1, int n2) {
+				return n1 % n2;
+			}
+			protected Object eval(long n1, long n2) {
+				return n1 % n2;
+			}
+			protected Object eval(float n1, float n2) {
+				return n1 % n2;
+			}
+			protected Object eval(double n1, double n2) {
+				return n1 % n2;
+			}
+			protected Object eval(BigDecimal n1, BigDecimal n2) {
+				return n1.remainder(n2);
+			}
+		};
+		new Math2Command(env, "min")
+		{
+			protected Object eval(int n1, int n2) {
+				return Math.min(n1, n2);
+			}
+			protected Object eval(long n1, long n2) {
+				return Math.min(n1, n2);
+			}
+			protected Object eval(float n1, float n2) {
+				return Math.min(n1, n2);
+			}
+			protected Object eval(double n1, double n2) {
+				return Math.min(n1, n2);
+			}
+			protected Object eval(BigDecimal n1, BigDecimal n2) {
+				return n1.min(n2);
+			}
+		};
+		new Math2Command(env, "max")
+		{
+			protected Object eval(int n1, int n2) {
+				return Math.max(n1, n2);
+			}
+			protected Object eval(long n1, long n2) {
+				return Math.max(n1, n2);
+			}
+			protected Object eval(float n1, float n2) {
+				return Math.max(n1, n2);
+			}
+			protected Object eval(double n1, double n2) {
+				return Math.max(n1, n2);
+			}
+			protected Object eval(BigDecimal n1, BigDecimal n2) {
+				return n1.max(n2);
+			}
+		};
+		// ---------------------------------------------------------------------
+		new Math1Command(env, "succ")
+		{
+			protected Object eval(int num) {
+				return num + 1;
+			}
+			protected Object eval(long num) {
+				return num + 1;
+			}
+			protected Object eval(float num) {
+				return num + 1;
+			}
+			protected Object eval(double num) {
+				return num + 1;
+			}
+			protected Object eval(BigDecimal num) {
+				return num.add(BigDecimal.ONE);
+			}
+		};
+		new Math1Command(env, "pred")
+		{
+			protected Object eval(int num) {
+				return num - 1;
+			}
+			protected Object eval(long num) {
+				return num - 1;
+			}
+			protected Object eval(float num) {
+				return num - 1;
+			}
+			protected Object eval(double num) {
+				return num - 1;
+			}
+			protected Object eval(BigDecimal num) {
+				return num.subtract(BigDecimal.ONE);
+			}
+		};
+		new Math1Command(env, "abs")
+		{
+			protected Object eval(int num) {
+				return Math.abs(num);
+			}
+			protected Object eval(long num) {
+				return Math.abs(num);
+			}
+			protected Object eval(float num) {
+				return Math.abs(num);
+			}
+			protected Object eval(double num) {
+				return Math.abs(num);
+			}
+			protected Object eval(BigDecimal num) {
+				return num.abs();
+			}
+		};
+		new Math1Command(env, "sign")
+		{
+			protected Object eval(int num) {
+				return Integer.signum(num);
+			}
+			protected Object eval(long num) {
+				return Long.signum(num);
+			}
+			protected Object eval(float num) {
+				return Math.signum(num);
+			}
+			protected Object eval(double num) {
+				return Math.signum(num);
+			}
+			protected Object eval(BigDecimal num) {
+				return num.signum();
+			}
+		};
+		new Command(env, "fact") {
+			public void exec(Machine m) {
+				BigDecimal num = (BigDecimal) m.pop();
+				BigDecimal res = BigDecimal.ONE;
+				while (num.compareTo(BigDecimal.ONE) > 0) {
+					res = res.multiply(num);
+					num = num.subtract(BigDecimal.ONE);
+				}
+				m.push(res);
+			}
+		};
+		new Command(env, "exp") {
+			public void exec(Machine m) {
+				final BigDecimal num = (BigDecimal) m.pop();
+				m.push(BigDecimal.valueOf(Math.exp(num.doubleValue())));
+			}
+		};
+		new Command(env, "log") {
+			public void exec(Machine m) {
+				final BigDecimal num = (BigDecimal) m.pop();
+				m.push(BigDecimal.valueOf(Math.log(num.doubleValue())));
+			}
+		};
+		//	fib == [1 0] dip [swap [+] unary] times popd;
+		//	nfib == [1 1] dip [dup [+ succ] dip swap] times pop;
+		//	gcd == [0 >] [dup rollup rem] while pop;
+	}
+
+	// =========================================================== Class Methods
+
+	static BigDecimal toBigDecimal(Number num) {
+		if ((num instanceof Double) || (num instanceof Float)) {
+			return BigDecimal.valueOf(num.doubleValue());
+		} else  {
+			return BigDecimal.valueOf(num.longValue());
+		}
+	}
+
+	public static Number parseNumber(String str) {
+		final int length = str.length();
+		if (length < 1) {
+			return null;
+		}
+		int p = 0;
+		char c = str.charAt(p);
+		if (c == '-' || c == '+') {
+			p++;
+			if (p == length) {
+				return null;
+			}
+			c = str.charAt(p);
+		}
+		if (c == '.') {
+			p++;
+			if (p == length) {
+				return null;
+			}
+			c = str.charAt(p);
+		}
+		if (!Character.isDigit(c)) {
+			return null;
+		}
+		final char last = str.charAt(length - 1);
+		switch (Character.toUpperCase(last)) {
+			case 'N':
+				str = str.substring(0, length - 1);
+				return new BigDecimal(str);
+			case 'D':
+				str = str.substring(0, length - 1);
+				return new Double(str);
+			case 'F':
+				str = str.substring(0, length - 1);
+				return Float.valueOf(str);
+			case 'L':
+				str = str.substring(0, length - 1);
+				return Long.valueOf(str);
+		}
+		try {
+			return Integer.parseInt(str);
+		} catch (NumberFormatException ex) {
+			// continue
+		}
+		try {
+			return Long.parseLong(str);
+		} catch (NumberFormatException ex) {
+			// continue
+		}
+		try {
+			return Double.parseDouble(str);
+		} catch (NumberFormatException ex) {
+			// continue
+		}
+		try {
+			return new BigDecimal(str);
+		} catch (NumberFormatException ex) {
+			return null;
+		}
+	}
+
+	public static void formatNumber(StringBuilder buf, Number num) {
+		buf.append(num);
+		if (num instanceof BigDecimal) {
+			buf.append('N');
+		} else if (num instanceof Long) {
+			buf.append('L');
+		} else if (num instanceof Float) {
+			buf.append('F');
+		}
+	}
+
+	// ========================================================== Nested Classes
+
+	static abstract class Math2Command extends Arg2Command
+	{
+		public Math2Command(Environment env, String name) {
+			super(env, name);
+		}
+
+		protected final void exec(Machine m, Object obj1, Object obj2) {
+			final Number num1 = (Number) obj1;
+			final Number num2 = (Number) obj2;
+			if (num1 instanceof BigDecimal) {
+				m.push(eval((BigDecimal) num1, toBigDecimal(num2)));
+			} else if (num2 instanceof BigDecimal) {
+				m.push(eval(toBigDecimal(num1), (BigDecimal) num2));
+			} else if ((num1 instanceof Double) || (num2 instanceof Double)) {
+				m.push(eval(num1.doubleValue(), num2.doubleValue()));
+			} else if ((num1 instanceof Float) || (num2 instanceof Float)) {
+				m.push(eval(num1.floatValue(), num2.floatValue()));
+			} else if ((num1 instanceof Long) || (num2 instanceof Long)) {
+				m.push(eval(num1.longValue(), num2.longValue()));
+			} else {
+				m.push(eval(num1.intValue(), num2.intValue()));
+			}
+		}
+
+		protected abstract Object eval(BigDecimal n1, BigDecimal n2);
+		protected abstract Object eval(double n1, double n2);
+		protected abstract Object eval(float n1, float n2);
+		protected abstract Object eval(long n1, long n2);
+		protected abstract Object eval(int n1, int n2);
+	}
+
+	// -------------------------------------------------------------------------
+
+	static abstract class Math1Command extends Command
+	{
+		public Math1Command(Environment env, String name) {
+			super(env, name);
+		}
+
+		public void exec(Machine m) {
+			final Number num = (Number) m.pop();
+			if (num instanceof BigDecimal) {
+				final BigDecimal bd = (BigDecimal) num;
+				m.push(eval(bd));
+			} else if (num instanceof Double) {
+				final Double d = (Double) num;
+				m.push(eval(d));
+			} else if (num instanceof Float) {
+				final Float f = (Float) num;
+				m.push(eval(f));
+			} else if (num instanceof Long) {
+				final Long l = (Long) num;
+				m.push(eval(l));
+			} else {
+				m.push(eval(num.intValue()));
+			}
+		}
+
+		protected abstract Object eval(BigDecimal num);
+		protected abstract Object eval(double num);
+		protected abstract Object eval(float num);
+		protected abstract Object eval(long num);
+		protected abstract Object eval(int num);
+	}
+}