From 4e2e42534925476e753ba6a44d04b35efcb1c5b8 Mon Sep 17 00:00:00 2001 From: TudbuT Date: Sat, 5 Mar 2022 20:55:03 +0100 Subject: [PATCH] first version of java interpreter --- bootstrap/ISBPL.java | 1114 ++++++++++++++++++++++++++++++++++++++++++ errorstream.isbpl | 9 + file.isbpl | 8 + std.isbpl | 100 ++-- 4 files changed, 1174 insertions(+), 57 deletions(-) create mode 100644 bootstrap/ISBPL.java create mode 100644 errorstream.isbpl create mode 100644 file.isbpl diff --git a/bootstrap/ISBPL.java b/bootstrap/ISBPL.java new file mode 100644 index 0000000..244fbca --- /dev/null +++ b/bootstrap/ISBPL.java @@ -0,0 +1,1114 @@ +import java.io.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Stack; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author TudbuT + * @since 04 Mar 2022 + */ + +public class ISBPL { + ArrayList types = new ArrayList<>(); + Stack> functionStack = new Stack<>(); + HashMap vars = new HashMap<>(); + ArrayList lastWords = new ArrayList<>(16); + int exitCode; + + public ISBPL() { + functionStack.push(new HashMap<>()); + } + + public ISBPLKeyword getKeyword(String word) { + switch (word) { + case "native": + return (idx, words, file, stack) -> { + idx++; + addNative(words[idx], file, stack); + return idx; + }; + case "func": + return this::createFunction; + case "def": + return (idx, words, file, stack) -> { + idx++; + Object var = new Object(); + functionStack.peek().put(words[idx], () -> stack.push(vars.get(var))); + functionStack.peek().put("=" + words[idx], () -> vars.put(var, stack.pop())); + return idx; + }; + case "if": + return (idx, words, file, stack) -> { + idx++; + AtomicInteger i = new AtomicInteger(idx); + ISBPLCallable callable = readBlock(i, words, file, stack, false); + if(stack.pop().isTruthy()) { + callable.call(); + } + return i.get(); + }; + case "while": + return (idx, words, file, stack) -> { + idx++; + AtomicInteger i = new AtomicInteger(idx); + ISBPLCallable cond = readBlock(i, words, file, stack, false); + i.getAndIncrement(); + ISBPLCallable block = readBlock(i, words, file, stack, false); + cond.call(); + while (stack.pop().isTruthy()) { + block.call(); + cond.call(); + } + return i.get(); + }; + case "stop": + return (idx, words, file, stack) -> { + ISBPLObject o = stack.pop(); + o.checkType(getType("int")); + throw new ISBPLStop((int) o.object); + }; + case "try": + return (idx, words, file, stack) -> { + idx++; + ISBPLObject array = stack.pop(); + array.checkTypeMulti(getType("array"), getType("string")); + String[] allowed; + if(array.type.name.equals("string")) { + allowed = new String[] { toJavaString(array) }; + } + else { + ISBPLObject[] arr = ((ISBPLObject[]) array.object); + allowed = new String[arr.length]; + for (int i = 0 ; i < arr.length ; i++) { + allowed[i] = toJavaString(arr[i]); + } + } + AtomicInteger i = new AtomicInteger(idx); + ISBPLCallable block = readBlock(i, words, file, stack, false); + i.getAndIncrement(); + ISBPLCallable catcher = readBlock(i, words, file, stack, false); + try { + block.call(); + } catch (ISBPLError error) { + if (Arrays.asList(allowed).contains(error.type) || allowed.length != 1 && allowed[0].equals("all")) { + stack.push(toISBPLString(error.type)); + stack.push(toISBPLString(error.message)); + catcher.call(); + } + else { + throw error; + } + } + return i.get(); + }; + case "do": + return (idx, words, file, stack) -> { + idx++; + AtomicInteger i = new AtomicInteger(idx); + ISBPLCallable block = readBlock(i, words, file, stack, false); + i.getAndIncrement(); + ISBPLCallable catcher = readBlock(i, words, file, stack, false); + try { + block.call(); + } finally { + catcher.call(); + } + return i.get(); + }; + default: + return null; + } + } + + @SuppressWarnings("RedundantCast") + private void addNative(String name, File file, Stack stack) { + ISBPLCallable func = null; + switch (name) { + case "alen": + func = () -> { + ISBPLObject o = stack.pop(); + o.checkType(getType("array")); + stack.push(new ISBPLObject(getType("int"), ((ISBPLObject[]) o.object).length)); + }; + break; + case "aget": + func = () -> { + ISBPLObject i = stack.pop(); + ISBPLObject o = stack.pop(); + i.checkType(getType("int")); + o.checkType(getType("array")); + stack.push(((ISBPLObject[]) o.object)[((int) i.object)]); + }; + break; + case "aput": + func = () -> { + ISBPLObject toPut = stack.pop(); + ISBPLObject i = stack.pop(); + ISBPLObject o = stack.pop(); + i.checkType(getType("int")); + o.checkType(getType("array")); + ((ISBPLObject[]) o.object)[((int) i.object)] = toPut; + }; + break; + case "anew": + func = () -> { + ISBPLObject i = stack.pop(); + i.checkType(getType("int")); + stack.push(new ISBPLObject(getType("array"), new ISBPLObject[((int) i.object)])); + }; + break; + case "_array": + func = () -> { + ISBPLObject a = stack.pop(); + if(a.type.equals(getType("array"))) + stack.push(a); + else if(a.object instanceof ISBPLObject[]) + stack.push(new ISBPLObject(getType("array"), a.object)); + else + typeError(a.type.name, "array"); + }; + break; + case "_char": + func = () -> { + ISBPLObject o = stack.pop(); + o.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double")); + stack.push(new ISBPLObject(getType("char"), ((char) o.toLong()))); + }; + break; + case "_byte": + func = () -> { + ISBPLObject o = stack.pop(); + o.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double")); + stack.push(new ISBPLObject(getType("byte"), ((byte) o.toLong()))); + }; + break; + case "_int": + func = () -> { + ISBPLObject o = stack.pop(); + o.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double")); + stack.push(new ISBPLObject(getType("int"), ((int) o.toLong()))); + }; + break; + case "_float": + func = () -> { + ISBPLObject o = stack.pop(); + o.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double")); + stack.push(new ISBPLObject(getType("float"), ((float) o.toDouble()))); + }; + break; + case "_long": + func = () -> { + ISBPLObject o = stack.pop(); + o.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double")); + stack.push(new ISBPLObject(getType("long"), o.toLong())); + }; + break; + case "_double": + func = () -> { + ISBPLObject o = stack.pop(); + o.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double")); + stack.push(new ISBPLObject(getType("double"), o.toDouble())); + }; + break; + case "ischar": + func = () -> { + ISBPLObject o = stack.pop(); + stack.push(new ISBPLObject(getType("int"), o.type.equals(getType("char")) ? 1 : 0)); + }; + break; + case "isbyte": + func = () -> { + ISBPLObject o = stack.pop(); + stack.push(new ISBPLObject(getType("int"), o.type.equals(getType("byte")) ? 1 : 0)); + }; + break; + case "isint": + func = () -> { + ISBPLObject o = stack.pop(); + stack.push(new ISBPLObject(getType("int"), o.type.equals(getType("int")) ? 1 : 0)); + }; + break; + case "isfloat": + func = () -> { + ISBPLObject o = stack.pop(); + stack.push(new ISBPLObject(getType("int"), o.type.equals(getType("float")) ? 1 : 0)); + }; + break; + case "islong": + func = () -> { + ISBPLObject o = stack.pop(); + stack.push(new ISBPLObject(getType("int"), o.type.equals(getType("long")) ? 1 : 0)); + }; + break; + case "isdouble": + func = () -> { + ISBPLObject o = stack.pop(); + stack.push(new ISBPLObject(getType("int"), o.type.equals(getType("double")) ? 1 : 0)); + }; + break; + case "isarray": + func = () -> { + ISBPLObject o = stack.pop(); + stack.push(new ISBPLObject(getType("int"), o.type.equals(getType("array")) ? 1 : 0)); + }; + break; + case "_layer_call": + func = () -> { + ISBPLObject i = stack.pop(); + ISBPLObject s = stack.pop(); + i.checkType(getType("int")); + functionStack.get(functionStack.size() - 1 - ((int) i.object)).get(toJavaString(s)).call(); + }; + break; + case "include": + func = () -> { + ISBPLObject s = stack.pop(); + String filepath = toJavaString(s); + processPath: + { + if (filepath.startsWith("/")) + break processPath; + if (filepath.startsWith("#")) { + filepath = System.getenv().getOrDefault("ISBPL_PATH", "/usr/lib/isbpl") + "/" + filepath.substring(1); + break processPath; + } + filepath = file.getParentFile().getAbsolutePath() + "/" + filepath; + } + File f = new File(filepath).getAbsoluteFile(); + try { + interpret(f, readFile(f), stack); + } + catch (IOException e) { + throw new ISBPLError("IO", "Couldn't find file " + filepath + " required by include keyword."); + } + }; + break; + case "putchar": + func = () -> { + ISBPLObject c = stack.pop(); + c.checkType(getType("char")); + System.out.print(((char) c.object)); + }; + break; + case "eputchar": + func = () -> { + ISBPLObject c = stack.pop(); + c.checkType(getType("char")); + System.err.print(((char) c.object)); + }; + break; + case "_file": + func = () -> { + ISBPLObject s = stack.pop(); + File f = new File(toJavaString(s)); + stack.push(new ISBPLObject(getType("file"), f)); + }; + break; + case "read": + func = () -> { + ISBPLObject end = stack.pop(); + ISBPLObject begin = stack.pop(); + ISBPLObject fileToRead = stack.pop(); + end.checkType(getType("int")); + begin.checkType(getType("int")); + fileToRead.checkType(getType("file")); + try { + FileInputStream f = new FileInputStream((File) fileToRead.object); + int b = ((int) begin.object); + int e = ((int) end.object); + byte[] bytes = new byte[e - b]; + f.read(bytes, b, e); + ISBPLObject[] arr = new ISBPLObject[bytes.length]; + for (int i = 0 ; i < arr.length ; i++) { + arr[i] = new ISBPLObject(getType("byte"), bytes[i]); + } + stack.push(new ISBPLObject(getType("array"), arr)); + } + catch (FileNotFoundException e) { + throw new ISBPLError("FileNotFound", "File not found."); + } + catch (IOException e) { + throw new ISBPLError("IO", "File couldn't be read from" + (e.getMessage() != null ? ": " + e.getMessage() : ".")); + } + }; + break; + case "flength": + func = () -> { + ISBPLObject f = stack.pop(); + f.checkType(getType("file")); + stack.push(new ISBPLObject(getType("int"), ((int) ((File) f.object).length()))); + }; + break; + case "write": + func = () -> { + ISBPLObject content = stack.pop(); + ISBPLObject fileToWrite = stack.pop(); + content.checkType(getType("array")); + fileToWrite.checkType(getType("file")); + throw new ISBPLError("NotImplemented", "_file write is not implemented"); + }; + break; + case "getos": + func = () -> { + // TODO: This is not done yet, and it's horrible so far. + stack.push(toISBPLString("linux")); + }; + break; + case "mktype": + func = () -> { + ISBPLObject s = stack.pop(); + ISBPLType type = registerType(toJavaString(s)); + stack.push(new ISBPLObject(getType("int"), type)); + }; + break; + case "typename": + func = () -> { + ISBPLObject i = stack.pop(); + i.checkType(getType("int")); + stack.push(toISBPLString(types.get(((int) i.object)).name)); + }; + break; + case "gettype": + func = () -> { + ISBPLObject o = stack.pop(); + stack.push(new ISBPLObject(getType("int"), o.type.id)); + }; + break; + case "throw": + func = () -> { + ISBPLObject message = stack.pop(); + ISBPLObject type = stack.pop(); + String msg = toJavaString(message); + String t = toJavaString(type); + throw new ISBPLError(t, msg); + }; + break; + case "exit": + func = () -> { + ISBPLObject code = stack.pop(); + code.checkType(getType("int")); + exitCode = ((int) code.object); + throw new ISBPLStop(0); + }; + break; + case "eq": + func = () -> { + ISBPLObject o1 = stack.pop(); + ISBPLObject o2 = stack.pop(); + stack.push(new ISBPLObject(getType("int"), o1.equals(o2) ? 1 : 0)); + }; + break; + case "gt": + func = () -> { + ISBPLObject o2 = stack.pop(); + ISBPLObject o1 = stack.pop(); + o1.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double")); + o2.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double")); + stack.push(new ISBPLObject(getType("int"), o1.toDouble() > o2.toDouble() ? 1 : 0)); + }; + break; + case "lt": + func = () -> { + ISBPLObject o2 = stack.pop(); + ISBPLObject o1 = stack.pop(); + o1.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double")); + o2.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double")); + stack.push(new ISBPLObject(getType("int"), o1.toDouble() < o2.toDouble() ? 1 : 0)); + }; + break; + case "not": + func = () -> { + stack.push(new ISBPLObject(getType("int"), stack.pop().isTruthy() ? 0 : 1)); + }; + break; + case "neg": + func = () -> { + ISBPLObject o = stack.pop(); + o.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double")); + stack.push(new ISBPLObject(o.type, o.negative())); + }; + break; + case "or": + func = () -> { + ISBPLObject o2 = stack.pop(); + ISBPLObject o1 = stack.pop(); + if(o1.isTruthy()) + stack.push(o1); + else + stack.push(o2); + }; + break; + case "and": + func = () -> { + ISBPLObject o2 = stack.pop(); + ISBPLObject o1 = stack.pop(); + // Pushes either 1 or the failed object + if (o1.isTruthy()) { + if (o2.isTruthy()) + stack.push(new ISBPLObject(getType("int"), 1)); + else + stack.push(o2); + } + else + stack.push(o1); + }; + break; + case "+": + func = () -> { + ISBPLObject o2 = stack.pop(); + ISBPLObject o1 = stack.pop(); + o1.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double")); + o2.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double")); + Object object1 = o1.object; + Object object2 = o2.object; + ISBPLObject r = null; + if(object1 instanceof Integer && object2 instanceof Integer) { + r = new ISBPLObject(getType("int"), (int) (Integer) object1 + (int) (Integer) object2); + } + if(object1 instanceof Long && object2 instanceof Long) { + r = new ISBPLObject(getType("long"), (long) (Long) object1 + (long) (Long) object2); + } + if(object1 instanceof Character && object2 instanceof Character) { + r = new ISBPLObject(getType("char"), (char) (Character) object1 + (char) (Character) object2); + } + if(object1 instanceof Byte && object2 instanceof Byte) { + r = new ISBPLObject(getType("byte"), Byte.toUnsignedInt((Byte) object1) + Byte.toUnsignedInt((Byte) object2)); + } + if(object1 instanceof Float && object2 instanceof Float) { + r = new ISBPLObject(getType("float"), (float) (Float) object1 + (float) (Float) object2); + } + if(object1 instanceof Double && object2 instanceof Double) { + r = new ISBPLObject(getType("double"), (double) (Double) object1 + (double) (Double) object2); + } + if(r != null) + stack.push(r); + else + typeError(o1.type.name, o2.type.name); + }; + break; + case "-": + func = () -> { + ISBPLObject o2 = stack.pop(); + ISBPLObject o1 = stack.pop(); + o1.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double")); + o2.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double")); + Object object1 = o1.object; + Object object2 = o2.object; + ISBPLObject r = null; + if(object1 instanceof Integer && object2 instanceof Integer) { + r = new ISBPLObject(getType("int"), (int) (Integer) object1 - (int) (Integer) object2); + } + if(object1 instanceof Long && object2 instanceof Long) { + r = new ISBPLObject(getType("long"), (long) (Long) object1 - (long) (Long) object2); + } + if(object1 instanceof Character && object2 instanceof Character) { + r = new ISBPLObject(getType("char"), (char) (Character) object1 - (char) (Character) object2); + } + if(object1 instanceof Byte && object2 instanceof Byte) { + r = new ISBPLObject(getType("byte"), Byte.toUnsignedInt((Byte) object1) - Byte.toUnsignedInt((Byte) object2)); + } + if(object1 instanceof Float && object2 instanceof Float) { + r = new ISBPLObject(getType("float"), (float) (Float) object1 - (float) (Float) object2); + } + if(object1 instanceof Double && object2 instanceof Double) { + r = new ISBPLObject(getType("double"), (double) (Double) object1 - (double) (Double) object2); + } + if(r != null) + stack.push(r); + else + typeError(o1.type.name, o2.type.name); + }; + break; + case "/": + func = () -> { + ISBPLObject o2 = stack.pop(); + ISBPLObject o1 = stack.pop(); + o1.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double")); + o2.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double")); + Object object1 = o1.object; + Object object2 = o2.object; + ISBPLObject r = null; + if(object1 instanceof Integer && object2 instanceof Integer) { + r = new ISBPLObject(getType("int"), (int) (Integer) object1 / (int) (Integer) object2); + } + if(object1 instanceof Long && object2 instanceof Long) { + r = new ISBPLObject(getType("long"), (long) (Long) object1 / (long) (Long) object2); + } + if(object1 instanceof Character && object2 instanceof Character) { + r = new ISBPLObject(getType("char"), (char) (Character) object1 / (char) (Character) object2); + } + if(object1 instanceof Byte && object2 instanceof Byte) { + r = new ISBPLObject(getType("byte"), Byte.toUnsignedInt((Byte) object1) / Byte.toUnsignedInt((Byte) object2)); + } + if(object1 instanceof Float && object2 instanceof Float) { + r = new ISBPLObject(getType("float"), (float) (Float) object1 / (float) (Float) object2); + } + if(object1 instanceof Double && object2 instanceof Double) { + r = new ISBPLObject(getType("double"), (double) (Double) object1 / (double) (Double) object2); + } + if(r != null) + stack.push(r); + else + typeError(o1.type.name, o2.type.name); + }; + break; + case "*": + func = () -> { + ISBPLObject o2 = stack.pop(); + ISBPLObject o1 = stack.pop(); + o1.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double")); + o2.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double")); + Object object1 = o1.object; + Object object2 = o2.object; + ISBPLObject r = null; + if(object1 instanceof Integer && object2 instanceof Integer) { + r = new ISBPLObject(getType("int"), (int) (Integer) object1 * (int) (Integer) object2); + } + if(object1 instanceof Long && object2 instanceof Long) { + r = new ISBPLObject(getType("long"), (long) (Long) object1 * (long) (Long) object2); + } + if(object1 instanceof Character && object2 instanceof Character) { + r = new ISBPLObject(getType("char"), (char) (Character) object1 * (char) (Character) object2); + } + if(object1 instanceof Byte && object2 instanceof Byte) { + r = new ISBPLObject(getType("byte"), Byte.toUnsignedInt((Byte) object1) * Byte.toUnsignedInt((Byte) object2)); + } + if(object1 instanceof Float && object2 instanceof Float) { + r = new ISBPLObject(getType("float"), (float) (Float) object1 * (float) (Float) object2); + } + if(object1 instanceof Double && object2 instanceof Double) { + r = new ISBPLObject(getType("double"), (double) (Double) object1 * (double) (Double) object2); + } + if(r != null) + stack.push(r); + else + typeError(o1.type.name, o2.type.name); + }; + break; + case "**": + func = () -> { + ISBPLObject o2 = stack.pop(); + ISBPLObject o1 = stack.pop(); + o1.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double")); + o2.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double")); + Object object1 = o1.object; + Object object2 = o2.object; + ISBPLObject r = null; + if(object1 instanceof Integer && object2 instanceof Integer) { + r = new ISBPLObject(getType("int"), Math.pow((int) (Integer) object1, (int) (Integer) object2)); + } + if(object1 instanceof Long && object2 instanceof Long) { + r = new ISBPLObject(getType("long"), Math.pow((long) (Long) object1, (long) (Long) object2)); + } + if(object1 instanceof Character && object2 instanceof Character) { + r = new ISBPLObject(getType("char"), Math.pow((char) (Character) object1, (char) (Character) object2)); + } + if(object1 instanceof Byte && object2 instanceof Byte) { + r = new ISBPLObject(getType("byte"), Math.pow(Byte.toUnsignedInt((Byte) object1), Byte.toUnsignedInt((Byte) object2))); + } + if(object1 instanceof Float && object2 instanceof Float) { + r = new ISBPLObject(getType("float"), Math.pow((float) (Float) object1, (float) (Float) object2)); + } + if(object1 instanceof Double && object2 instanceof Double) { + r = new ISBPLObject(getType("double"), Math.pow((double) (Double) object1, (double) (Double) object2)); + } + if(r != null) + stack.push(r); + else + typeError(o1.type.name, o2.type.name); + }; + break; + case "%": + func = () -> { + ISBPLObject o2 = stack.pop(); + ISBPLObject o1 = stack.pop(); + o1.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double")); + o2.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double")); + Object object1 = o1.object; + Object object2 = o2.object; + ISBPLObject r = null; + if(object1 instanceof Integer && object2 instanceof Integer) { + r = new ISBPLObject(getType("int"), (int) (Integer) object1 % (int) (Integer) object2); + } + if(object1 instanceof Long && object2 instanceof Long) { + r = new ISBPLObject(getType("long"), (long) (Long) object1 % (long) (Long) object2); + } + if(object1 instanceof Character && object2 instanceof Character) { + r = new ISBPLObject(getType("char"), (char) (Character) object1 % (char) (Character) object2); + } + if(object1 instanceof Byte && object2 instanceof Byte) { + r = new ISBPLObject(getType("byte"), Byte.toUnsignedInt((Byte) object1) % Byte.toUnsignedInt((Byte) object2)); + } + if(object1 instanceof Float && object2 instanceof Float) { + r = new ISBPLObject(getType("float"), (float) (Float) object1 % (float) (Float) object2); + } + if(object1 instanceof Double && object2 instanceof Double) { + r = new ISBPLObject(getType("double"), (double) (Double) object1 % (double) (Double) object2); + } + if(r != null) + stack.push(r); + else + typeError(o1.type.name, o2.type.name); + }; + break; + case "^": + func = () -> { + ISBPLObject o2 = stack.pop(); + ISBPLObject o1 = stack.pop(); + o1.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("long")); + o2.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("long")); + Object object1 = o1.object; + Object object2 = o2.object; + ISBPLObject r = null; + if(object1 instanceof Integer && object2 instanceof Integer) { + r = new ISBPLObject(getType("int"), (int) (Integer) object1 ^ (int) (Integer) object2); + } + if(object1 instanceof Long && object2 instanceof Long) { + r = new ISBPLObject(getType("long"), (long) (Long) object1 ^ (long) (Long) object2); + } + if(object1 instanceof Character && object2 instanceof Character) { + r = new ISBPLObject(getType("char"), (char) (Character) object1 ^ (char) (Character) object2); + } + if(object1 instanceof Byte && object2 instanceof Byte) { + r = new ISBPLObject(getType("byte"), Byte.toUnsignedInt((Byte) object1) ^ Byte.toUnsignedInt((Byte) object2)); + } + if(r != null) + stack.push(r); + else + typeError(o1.type.name, o2.type.name); + }; + break; + case "dup": + func = () -> { + ISBPLObject o = stack.pop(); + stack.push(new ISBPLObject(o.type, o.object)); + stack.push(new ISBPLObject(o.type, o.object)); + }; + break; + case "pop": + func = stack::pop; + break; + case "swap": + func = () -> { + ISBPLObject o1 = stack.pop(); + ISBPLObject o2 = stack.pop(); + stack.push(o2); + stack.push(o1); + }; + break; + } + functionStack.peek().put(name, func); + } + + private int createFunction(int i, String[] words, File file, Stack stack) { + i++; + String name = words[i]; + AtomicInteger integer = new AtomicInteger(++i); + ISBPLCallable callable = readBlock(integer, words, file, stack, true); + i = integer.get(); + functionStack.peek().put(name, callable); + return i; + } + + private ISBPLCallable readBlock(AtomicInteger idx, String[] words, File file, Stack stack, boolean isFunction) { + ArrayList newWords = new ArrayList<>(); + int i = idx.get(); + i++; + int lvl = 1; + for (; i < words.length && lvl > 0 ; i++) { + String word = words[i]; + if(word.equals("{")) + lvl++; + if(word.equals("}")) { + if(--lvl == 0) + break; + } + newWords.add(word); + } + idx.set(i); + String[] theWords = newWords.toArray(new String[0]); + return () -> interpretRaw(file, theWords, stack, isFunction); + } + + public String toJavaString(ISBPLObject string) { + string.checkType(getType("string")); + ISBPLObject[] array = ((ISBPLObject[]) string.object); + char[] chars = new char[array.length]; + for (int i = 0 ; i < array.length ; i++) { + chars[i] = ((char) array[i].object); + } + return new String(chars); + } + + public ISBPLObject toISBPLString(String s) { + char[] chars = s.toCharArray(); + ISBPLObject[] objects = new ISBPLObject[chars.length]; + ISBPLType type = getType("char"); + for (int i = 0 ; i < chars.length ; i++) { + objects[i] = new ISBPLObject(type, chars[i]); + } + return new ISBPLObject(getType("string"), objects); + } + + public ISBPLType registerType(String name) { + ISBPLType type = new ISBPLType(name); + types.add(type); + return type; + } + + // These will die as soon as std creates the real types and any types created before these are replaced become invalid. + static final ISBPLType defaultTypeInt = new ISBPLType("int"); + static final ISBPLType defaultTypeString = new ISBPLType("string"); + + public ISBPLType getType(String name) { + for (int i = 0 ; i < types.size() ; i++) { + if(types.get(i).name.equals(name)) + return types.get(i); + } + if(name.equals("int")) + return defaultTypeInt; + if(name.equals("string")) + return defaultTypeString; + return null; + } + public void typeError(String got, String wanted) { + throw new ISBPLError("IncompatibleTypes", "Incompatible types: " + got + " - " + wanted); + } + + public void interpret(File file, String code, Stack stack) { + code = cleanCode(code); + String[] words = splitWords(code); + interpretRaw(file, words, stack, false); + } + + private void interpretRaw(File file, String[] words, Stack stack, boolean isFunction) { + if(isFunction) + functionStack.push(new HashMap<>()); + try { + for (int i = 0 ; i < words.length ; i++) { + String word = words[i]; + if (word.length() == 0) + continue; + lastWords.add(0, word); + while(lastWords.size() > 16) + lastWords.remove(lastWords.size() - 1); + ISBPLKeyword keyword = getKeyword(word); + if (keyword != null) { + i = keyword.call(i, words, file, stack); + continue; + } + ISBPLCallable func = functionStack.peek().get(word); + if(func != null) { + func.call(); + continue; + } + func = functionStack.get(0).get(word); + if(func != null) { + func.call(); + continue; + } + if (word.startsWith("\"")) { + stack.push(toISBPLString(word.substring(1))); + continue; + } + try { + stack.push(new ISBPLObject(getType("int"), Integer.parseInt(word))); + continue; + } catch (Exception ignore) {} + try { + stack.push(new ISBPLObject(getType("long"), Long.parseLong(word))); + continue; + } catch (Exception ignore) {} + try { + stack.push(new ISBPLObject(getType("float"), Float.parseFloat(word))); + continue; + } catch (Exception ignore) {} + try { + stack.push(new ISBPLObject(getType("double"), Double.parseDouble(word))); + continue; + } catch (Exception ignore) {} + throw new ISBPLError("InvalidWord", word + " is not a function, object, or keyword."); + } + } catch (ISBPLStop stop) { + if(stop.amount == 0) + return; + throw new ISBPLStop(stop.amount); + } finally { + if(isFunction) + functionStack.pop(); + } + } + + // Magic, please test before pushing changes! + private String[] splitWords(String code) { + ArrayList words = new ArrayList<>(); + char[] chars = code.toCharArray(); + boolean isInString = false; + boolean escaping = false; + String word = ""; + for (int i = 0 ; i < chars.length ; i++) { + char c = chars[i]; + if(isInString) { + if(c == '\\') { + escaping = !escaping; + if(escaping) + continue; + } + if(c == 'n' && escaping) { + word += '\n'; + escaping = false; + continue; + } + if(c == 'r' && escaping) { + escaping = false; + word += '\r'; + continue; + } + if(c == '"') { + if (escaping) { + escaping = false; + } + else { + isInString = false; + continue; + } + } + word += c; + if(escaping) + throw new RuntimeException("Error parsing code: Invalid Escape."); + } + else if(c == '"' && word.length() == 0) { + word += '"'; + isInString = true; + } + else if(c == ' ') { + words.add(word); + word = ""; + } + else { + word += c; + } + } + words.add(word); + return words.toArray(new String[0]); + } + + private String cleanCode(String code) { + return code + .replaceAll("\r", "\n") + .replaceAll("\n", " ") + ; + } + + public static void main(String[] args) throws IOException { + Stack stack = new Stack<>(); + ISBPL isbpl = new ISBPL(); + try { + File std = new File(System.getenv().getOrDefault("ISBPL_PATH", "/usr/lib/isbpl") + "/std.isbpl"); + isbpl.interpret(std, readFile(std), stack); + File file = new File(args[0]).getAbsoluteFile(); + isbpl.interpret(file, readFile(file), stack); + stack.push(argarray(isbpl, args)); + isbpl.interpret(file, "main exit", stack); + } catch (ISBPLStop stop) { + System.exit(isbpl.exitCode); + } catch (Exception e) { + e.printStackTrace(); + System.out.println(stack); + } + } + + private static ISBPLObject argarray(ISBPL isbpl, String[] args) { + ISBPLObject[] array = new ISBPLObject[args.length - 1]; + for (int i = 1 ; i < args.length ; i++) { + array[i - 1] = new ISBPLObject(isbpl.getType("string"), isbpl.toISBPLString(args[i])); + } + return new ISBPLObject(isbpl.getType("array"), array); + } + + private static String readFile(File f) throws IOException { + FileInputStream fis = new FileInputStream(f); + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + byte[] currentBytes = new byte[4096]; + int len; + while ((len = fis.read(currentBytes)) > 0) { + bytes.write(currentBytes, 0, len); + } + return bytes.toString(); + } +} + +interface ISBPLKeyword { + int call(int idx, String[] words, File file, Stack stack); +} + +interface ISBPLCallable { + void call(); +} + +class ISBPLType { + static int gid = -2; + int id = gid++; + String name; + + public ISBPLType(String name) { + this.name = name; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ISBPLType)) return false; + ISBPLType type = (ISBPLType) o; + return id == type.id; + } + + @Override + public int hashCode() { + return id; + } +} + +class ISBPLObject { + final ISBPLType type; + final Object object; + + public ISBPLObject(ISBPLType type, Object object) { + this.type = type; + this.object = object; + } + + public boolean isTruthy() { + return object != null && object != Integer.valueOf(0); + } + + // This has heavy optimizations, please do not change unless necessary + public boolean equals(Object o) { + if(this == o) return true; + if(!(o instanceof ISBPLObject)) return false; + ISBPLObject object = (ISBPLObject) o; + if(this.object == object.object) + return true; + if(this.object == null) + return false; + if(object.object == null) + return false; + if(this.object.getClass().isArray() || object.object.getClass().isArray()) { + if(this.object.getClass().isArray() && object.object.getClass().isArray()) { + return Arrays.equals((Object[]) this.object, (Object[]) object.object); + } + else { + return false; + } + } + return this.object.equals(object.object); + } + + public void checkType(ISBPLType wanted) { + if(wanted.id != type.id) { + throw new ISBPLError("IncompatibleTypes", "Incompatible types: " + type.name + " - " + wanted.name); + } + } + + public int checkTypeMulti(ISBPLType... wanted) { + int f = -1; + String wantedNames = ""; + for (int i = 0 ; i < wanted.length ; i++) { + wantedNames += " " + wanted[i].name; + if(wanted[i].id == type.id) { + f = i; + break; + } + } + if(f == -1) { + throw new ISBPLError("IncompatibleTypes", "Incompatible types: " + type.name + " - " + wantedNames.substring(1)); + } + return f; + } + + public double toDouble() { + if(object instanceof Integer) { + return (double) (int) (Integer) object; + } + if(object instanceof Long) { + return (double) (long) (Long) object; + } + if(object instanceof Character) { + return (double) (char) (Character) object; + } + if(object instanceof Byte) { + return Byte.toUnsignedInt((Byte) object); + } + if(object instanceof Float) { + return (double) (float) (Float) object; + } + if(object instanceof Double) { + return (double) (Double) object; + } + throw new ISBPLError("InvalidArgument", "The argument is not a number."); + } + + public long toLong() { + if(object instanceof Integer) { + return (long) (int) (Integer) object; + } + if(object instanceof Long) { + return (long) (Long) object; + } + if(object instanceof Character) { + return (long) (char) (Character) object; + } + if(object instanceof Byte) { + return Byte.toUnsignedInt((Byte) object); + } + if(object instanceof Float) { + return (long) (float) (Float) object; + } + if(object instanceof Double) { + return (long) (double) (Double) object; + } + throw new ISBPLError("InvalidArgument", "The argument is not a number."); + } + + public Object negative() { + if(object instanceof Integer) { + return -(int) (Integer) object; + } + if(object instanceof Long) { + return -(long) (Long) object; + } + if(object instanceof Float) { + return -(float) (Float) object; + } + if(object instanceof Double) { + return -(double) (Double) object; + } + throw new ISBPLError("InvalidArgument", "This type of number can't be negated!"); + } +} + +class ISBPLError extends RuntimeException { + final String type; + final String message; + + public ISBPLError(String type, String message) { + this.type = type; + this.message = message; + } + + @Override + public String getMessage() { + return type + ": " + message; + } +} + +class ISBPLStop extends RuntimeException { + + int amount; + + public ISBPLStop(int amount) { + this.amount = amount - 1; + } +} diff --git a/errorstream.isbpl b/errorstream.isbpl new file mode 100644 index 0000000..dd405b3 --- /dev/null +++ b/errorstream.isbpl @@ -0,0 +1,9 @@ +native eputchar + +func eputs { + def str _string =str + 1 neg =i + while { ( i 1 + =i ) ( i str alen lt ) } { + str i aget eputchar + } +} diff --git a/file.isbpl b/file.isbpl new file mode 100644 index 0000000..28deb5c --- /dev/null +++ b/file.isbpl @@ -0,0 +1,8 @@ +native _file +native isfile + +def TYPE_FILE "file" mktype =TYPE_FILE + +native read +native flength +native write diff --git a/std.isbpl b/std.isbpl index a41b421..3c516a4 100644 --- a/std.isbpl +++ b/std.isbpl @@ -1,39 +1,29 @@ +native dup +native pop +native swap + +func # { pop } + native alen native aget native aput native anew native _array -func __array { - "NotImplemented" "Not implemented" throw -} native _char +native _byte native _int -native _file native _float native _long native _double -func __char { - _char -} -func __int { - _int -} -func __file { - _file -} -native __float -func __long { - _long -} -native __double - native ischar +native isbyte native isint native isfloat native islong native isdouble +native isarray "call a dynamically computed function" # "Please keep in mind that this will throw a native error if" # @@ -41,27 +31,21 @@ native isdouble "instantly crash the program." # native _layer_call func call { - 0 _layer_call + 1 _layer_call } -"This returns the last word as a string, from an index." # -"index has to be <=15, index 0 is this call" # +"This returns the last word as a string, from an index." # +"index has to be <16, index 0 is this call" # native _last_word native include native putchar -native read -native flength -native write -native type native mktype -native typename native getos -native istype +native typename +native gettype native settype -"try and catch are keywords, not functions" # +"try and do are keywords, not functions" # native throw native exit -"storelength" # -native stlen native eq native gt @@ -78,22 +62,18 @@ native ** native % native ^ -native dup -native pop -native swap - func ++ { 1 + } func -- { 1 - } "this looks so wrong" # func ( { } func ) { } -func # { pop } -func ; { -1 } +func ; { } -def TYPE_CHAR "char" mktype =TYPE_CHAR +"int must be registered first." # def TYPE_INT "int" mktype =TYPE_INT -def TYPE_FILE "file" mktype =TYPE_FILE +def TYPE_CHAR "char" mktype =TYPE_CHAR +def TYPE_BYTE "byte" mktype =TYPE_BYTE def TYPE_FLOAT "float" mktype =TYPE_FLOAT def TYPE_LONG "long" mktype =TYPE_LONG def TYPE_DOUBLE "double" mktype =TYPE_DOUBLE @@ -104,7 +84,11 @@ func _string { def object =object object ischar if { - 0 _char 1 anew =object + 1 anew =object + object TYPE_STRING settype + } + object isbyte if { + itos =object } object isint if { itos =object @@ -118,14 +102,13 @@ func _string { object isdouble if { dtoi =object } - - object -} -func __string { - _string + + object isstring not if { + "IncompatibleTypes" "Incompatible types: " object gettype typename " - string" strconcat strconcat throw + } } func isstring { - isarray + gettype typename "string" eq } func itos { @@ -150,7 +133,7 @@ func ntos { def n =n def negative n 0 lt dup if { n neg =n 1 =negative } =negative - def thetype n type =thetype + def thetype n gettype =thetype while { n 1 lt not } { n ( n 10 % dup =x ) - =n @@ -173,7 +156,7 @@ func ston { def s =s "bind string" # def n 0 =n "the number to return" # def negative def idx s 0 aget '-' eq dup =idx =negative - + while { idx s alen lt } { def chr s idx aget =chr n ( chr ( 10 ( s alen idx - 1 - ) ** ) + =n @@ -202,9 +185,9 @@ func strconcat { "bind args" # def str2 _string =str2 def str1 _string =str1 - + def str 0 _char str1 alen str2 alen + anew =str - + str1 str 0 0 str1 alen acopy str2 str 0 str1 alen str2 alen acopy @@ -267,8 +250,8 @@ func strsub { } func puts { - def str _string =str - 1 neg =i + def str _array =str + def i 1 neg =i while { ( i 1 + =i ) ( i str alen lt ) } { str i aget putchar } @@ -277,7 +260,7 @@ func puts { func anewput { def size =size def arr size anew =arr - + def i 0 =i while { i size lt } { arr swap i swap aput @@ -328,14 +311,14 @@ func strcontains { "These functions are magic, they use natives to get recent words and modify the" # "variables, do not touch them unless you know exactly what you're doing!" # func inc { - def varname 3 _last_word =varname + def varname 4 _last_word =varname pop ( varname 1 _layer_call ) "Get var" # 1 + "Increment" # ( "=" varname strconcat 1 _layer_call ) "Store var" # } func dec { - def varname 3 _last_word =varname + def varname 4 _last_word =varname pop ( varname 1 _layer_call ) "Get var" # 1 - "Decrement" # @@ -343,8 +326,9 @@ func dec { } func random { - getos dup ( "linux" strcontains ) swap ( "macos" strcontains ) or dup if { - "/dev/urandom" _file 1 read + "file.isbpl" include + getos strlowercase dup ( "linux" strcontains ) swap ( "macos" strcontains ) or dup if { + "/dev/urandom" _file 0 1 read } not if { "__windows_radom" call @@ -361,6 +345,7 @@ func strlowercase { } i inc } + s } func struppercase { @@ -373,4 +358,5 @@ func struppercase { } i inc } + s }