mirror of
https://github.com/zeroflag/fcl.git
synced 2025-01-11 20:01:10 +01:00
package moves
This commit is contained in:
parent
5b7f7c7562
commit
2c68ffa348
26 changed files with 2257 additions and 0 deletions
53
Dictionary.java
Normal file
53
Dictionary.java
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package com.vectron.fcl;
|
||||||
|
|
||||||
|
import com.vectron.fcl.types.Word;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class Dictionary {
|
||||||
|
private final List<Word> dict = new ArrayList<>();
|
||||||
|
private final Set<String> immediate = new HashSet<>();
|
||||||
|
|
||||||
|
public Dictionary() {
|
||||||
|
immediate.addAll(Arrays.asList(";", "immediate", "override"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Word word) {
|
||||||
|
dict.add(word);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Word at(String name) {
|
||||||
|
for (int i = dict.size() - 1; i >= 0; i--) {
|
||||||
|
Word each = dict.get(i);
|
||||||
|
if (each.visible() && name.equals(each.name()))
|
||||||
|
return each;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(String name) {
|
||||||
|
Word exiting = at(name);
|
||||||
|
if (exiting != null)
|
||||||
|
dict.remove(exiting);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isImmediate(String name) {
|
||||||
|
return immediate.contains(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void makeImmediate(Word word) {
|
||||||
|
immediate.add(word.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> wordList() {
|
||||||
|
Set<String> result = new HashSet<>();
|
||||||
|
for (Word word : dict) {
|
||||||
|
result.add(word.name());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
585
Fcl.java
Normal file
585
Fcl.java
Normal file
|
@ -0,0 +1,585 @@
|
||||||
|
package com.vectron.fcl;
|
||||||
|
|
||||||
|
import com.vectron.fcl.exceptions.Aborted;
|
||||||
|
import com.vectron.fcl.exceptions.TypeMismatched;
|
||||||
|
import com.vectron.fcl.interop.JvmInterOp;
|
||||||
|
import com.vectron.fcl.types.ArithmeticOperand;
|
||||||
|
import com.vectron.fcl.types.Bool;
|
||||||
|
import com.vectron.fcl.types.LogicOperand;
|
||||||
|
import com.vectron.fcl.types.Nil;
|
||||||
|
import com.vectron.fcl.types.Num;
|
||||||
|
import com.vectron.fcl.types.Obj;
|
||||||
|
import com.vectron.fcl.types.Primitive;
|
||||||
|
import com.vectron.fcl.types.Str;
|
||||||
|
import com.vectron.fcl.types.Word;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class Fcl {
|
||||||
|
public static final boolean STRICT = true;
|
||||||
|
private static final String EXIT = "exit";
|
||||||
|
private final int SCRATCH_SIZE = 1024;
|
||||||
|
private enum Mode { COMPILE, INTERPRET }
|
||||||
|
private final Dictionary dict = new Dictionary();
|
||||||
|
private final FclStack rstack = new FclStack();
|
||||||
|
private final JvmInterOp interOp;
|
||||||
|
private final FclStack stack;
|
||||||
|
private final Transcript transcript;
|
||||||
|
private Word lastWord;
|
||||||
|
private Reader reader;
|
||||||
|
private Mode mode = Mode.INTERPRET;
|
||||||
|
private final Object[] heap;
|
||||||
|
private int dp = SCRATCH_SIZE;
|
||||||
|
private int ip = 0;
|
||||||
|
|
||||||
|
class ColonDef implements Word {
|
||||||
|
private final int address;
|
||||||
|
private final String name;
|
||||||
|
private boolean visible = true;
|
||||||
|
|
||||||
|
public ColonDef(int address, String name) {
|
||||||
|
this.address = address;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enter() {
|
||||||
|
rstack.push(new Num(ip));
|
||||||
|
innerLoop(address);
|
||||||
|
ip = rstack.pop().intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String name() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visible(boolean isVisible) {
|
||||||
|
this.visible = isVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean visible() {
|
||||||
|
return visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "xt_" + name + " (" + address + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long longValue() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int intValue() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double doubleValue() {
|
||||||
|
throw new TypeMismatched(this, "double");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean boolValue() {
|
||||||
|
throw new TypeMismatched(this, "bool");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Num asNum() {
|
||||||
|
if (STRICT) throw new TypeMismatched(this, "num");
|
||||||
|
return Num.NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Str asStr() {
|
||||||
|
return new Str(toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object value() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Obj other) {
|
||||||
|
return other instanceof ColonDef
|
||||||
|
? name.compareTo(((ColonDef) other).name)
|
||||||
|
: -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Var implements Word {
|
||||||
|
private final int address;
|
||||||
|
private final String name;
|
||||||
|
private boolean visible = true;
|
||||||
|
|
||||||
|
public Var(int address, String name) {
|
||||||
|
this.address = address;
|
||||||
|
this.name = name;
|
||||||
|
heap[address] = new Num(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visible(boolean isVisible) {
|
||||||
|
this.visible = isVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean visible() {
|
||||||
|
return visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enter() {
|
||||||
|
stack.push(new Num(address));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String name() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "var_" + name + " (" + address + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long longValue() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int intValue() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double doubleValue() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean boolValue() {
|
||||||
|
throw new TypeMismatched(this, "bool");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Num asNum() {
|
||||||
|
if (STRICT) throw new TypeMismatched(this, "num");
|
||||||
|
return Num.NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Str asStr() {
|
||||||
|
return new Str(toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object value() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Obj other) {
|
||||||
|
return other instanceof Var
|
||||||
|
? name.compareTo(((Var) other).name)
|
||||||
|
: -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Val implements Word {
|
||||||
|
private final String name;
|
||||||
|
private final Obj value;
|
||||||
|
private boolean visible = true;
|
||||||
|
|
||||||
|
public Val(String name, Obj value) {
|
||||||
|
this.name = name;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visible(boolean isVisible) {
|
||||||
|
this.visible = isVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean visible() {
|
||||||
|
return visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enter() {
|
||||||
|
stack.push(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String name() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "val_" + name + " (" + value + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long longValue() {
|
||||||
|
return value.longValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int intValue() {
|
||||||
|
return value.intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double doubleValue() {
|
||||||
|
return value.doubleValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean boolValue() {
|
||||||
|
return value.boolValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Num asNum() {
|
||||||
|
if (STRICT) throw new TypeMismatched(this, "num");
|
||||||
|
return Num.NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Str asStr() {
|
||||||
|
return new Str(toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object value() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Obj other) {
|
||||||
|
return other instanceof Val
|
||||||
|
? name.compareTo(((Val) other).name)
|
||||||
|
: -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Fcl(FclStack stack, int heapSize, Transcript transcript) {
|
||||||
|
this.stack = stack;
|
||||||
|
this.heap = new Object[heapSize];
|
||||||
|
this.interOp = new JvmInterOp(stack);
|
||||||
|
this.transcript = transcript;
|
||||||
|
initPrimitives();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initPrimitives() {
|
||||||
|
addPrimitive("+", () -> stack.push((aOp(stack.pop())).add(stack.pop())));
|
||||||
|
addPrimitive("-", () -> {
|
||||||
|
Obj top = stack.pop();
|
||||||
|
stack.push(aOp(stack.pop()).sub(top));
|
||||||
|
});
|
||||||
|
addPrimitive("*", () -> stack.push((aOp(stack.pop())).mul(stack.pop())));
|
||||||
|
addPrimitive("/", () -> {
|
||||||
|
Obj top = stack.pop();
|
||||||
|
stack.push((aOp(stack.pop())).div(top));
|
||||||
|
});
|
||||||
|
addPrimitive("/mod", () -> {
|
||||||
|
Num b = stack.pop().asNum();
|
||||||
|
Num a = stack.pop().asNum();
|
||||||
|
stack.push(a.mod(b));
|
||||||
|
stack.push(a.intDiv(b));
|
||||||
|
});
|
||||||
|
addPrimitive("pow", () -> {
|
||||||
|
Num exponent = stack.pop().asNum();
|
||||||
|
Num base = stack.pop().asNum();
|
||||||
|
stack.push(base.power(exponent));
|
||||||
|
});
|
||||||
|
addPrimitive("and", () -> stack.push(lOp(stack.pop()).and(stack.pop())));
|
||||||
|
addPrimitive("or", () -> stack.push((lOp(stack.pop())).or(stack.pop())));
|
||||||
|
addPrimitive("not", () -> stack.push((lOp(stack.pop())).not()));
|
||||||
|
addPrimitive("drop", stack::pop);
|
||||||
|
addPrimitive("dup", () -> stack.push(stack.peek()));
|
||||||
|
addPrimitive("swap", () -> {
|
||||||
|
Obj a = stack.pop();
|
||||||
|
Obj b = stack.pop();
|
||||||
|
stack.push(a);
|
||||||
|
stack.push(b);
|
||||||
|
});
|
||||||
|
addPrimitive("rswap", () -> {
|
||||||
|
Obj a = rstack.pop();
|
||||||
|
Obj b = rstack.pop();
|
||||||
|
rstack.push(a);
|
||||||
|
rstack.push(b);
|
||||||
|
});
|
||||||
|
addPrimitive(EXIT, () -> {});
|
||||||
|
addPrimitive("clean", stack::clean);
|
||||||
|
addPrimitive("depth", () -> stack.push(new Num(stack.size())));
|
||||||
|
addPrimitive("=", () -> stack.push(stack.pop().equals(stack.pop()) ? Bool.TRUE : Bool.FALSE));
|
||||||
|
addPrimitive("<", () -> stack.push(stack.pop().asNum().greater(stack.pop().asNum())));
|
||||||
|
addPrimitive("true", () -> stack.push(Bool.TRUE));
|
||||||
|
addPrimitive("false", () -> stack.push(Bool.FALSE));
|
||||||
|
addPrimitive("nil", () -> stack.push(Nil.INSTANCE));
|
||||||
|
addPrimitive("here", () -> stack.push(new Num(dp)));
|
||||||
|
addPrimitive("interpret", () -> mode = Mode.INTERPRET);
|
||||||
|
addPrimitive("lit", () -> stack.push((Obj)heap[ip++]));
|
||||||
|
addPrimitive(">r", () -> rstack.push(stack.pop()));
|
||||||
|
addPrimitive("r>", () -> stack.push(rstack.pop()));
|
||||||
|
addPrimitive("i", () -> stack.push(rstack.peek()));
|
||||||
|
addPrimitive("j", () -> stack.push(rstack.at(1)));
|
||||||
|
addPrimitive(",", () -> heap[dp++] = stack.pop());
|
||||||
|
addPrimitive("!", () -> heap[stack.pop().intValue()] = stack.pop());
|
||||||
|
addPrimitive("@", () -> stack.push((Obj) heap[stack.pop().intValue()]));
|
||||||
|
addPrimitive("[']", () -> stack.push((Word)heap[ip++]));
|
||||||
|
addPrimitive("`", () -> { Word word = dict.at(word()); stack.push(word == null ? Nil.INSTANCE : word); });
|
||||||
|
addPrimitive("immediate", () -> dict.makeImmediate(lastWord));
|
||||||
|
addPrimitive(".", () -> show(stack.pop()));
|
||||||
|
addPrimitive("jvm-call-static", interOp::jvmCallStatic);
|
||||||
|
addPrimitive("jvm-call-method", interOp::jvmCallMethod);
|
||||||
|
addPrimitive("jvm-static-var", interOp::jvmStaticVar);
|
||||||
|
addPrimitive("jvm-null", () -> stack.push(null));
|
||||||
|
addPrimitive("asc*", this::sortAsc);
|
||||||
|
addPrimitive("dsc*", this::sortDsc);
|
||||||
|
addPrimitive("rev*", this::reverse);
|
||||||
|
addPrimitive("key", () -> stack.push(new Num(key())));
|
||||||
|
addPrimitive("word", () -> stack.push(new Str(word())));
|
||||||
|
addPrimitive("override", () -> lastWord.visible(false));
|
||||||
|
addPrimitive("reveal", () -> lastWord.visible(true));
|
||||||
|
addPrimitive("delword", () -> dict.remove((String)stack.pop().value()));
|
||||||
|
addPrimitive("jmp#f", () -> ip += stack.pop().boolValue() ? 1 : ((Num) heap[ip]).longValue());
|
||||||
|
addPrimitive("jmp", () -> ip += ((Num) heap[ip]).longValue());
|
||||||
|
addPrimitive("allot", () -> { int p = dp; dp += stack.pop().longValue(); stack.push(new Num(p)); });
|
||||||
|
addPrimitive("freemem", () -> stack.push(new Num(heap.length - dp)));
|
||||||
|
addPrimitive("var:", () -> { String name = word(); dict.add(new Var(dp, name)); dp++; });
|
||||||
|
addPrimitive("val:", () -> { String name = word(); dict.add(new Val(name, stack.pop())); });
|
||||||
|
addPrimitive("abort", () -> { throw new Aborted(stack.pop().asStr().value()); });
|
||||||
|
addPrimitive("exec", () -> {
|
||||||
|
rstack.push(new Num(ip));
|
||||||
|
innerLoop(pop().intValue());
|
||||||
|
ip = rstack.pop().intValue();
|
||||||
|
});
|
||||||
|
addPrimitive("create", () -> dict.add(new ColonDef(dp, (String)stack.pop().value())));
|
||||||
|
addPrimitive("dasm", this::disassemble);
|
||||||
|
addPrimitive(":", () -> {
|
||||||
|
lastWord = new ColonDef(dp, word());
|
||||||
|
dict.add(lastWord);
|
||||||
|
mode = Mode.COMPILE;
|
||||||
|
});
|
||||||
|
addPrimitive(";", () -> {
|
||||||
|
heap[dp++] = dict.at(EXIT);
|
||||||
|
heap[dp++] = Nil.INSTANCE;
|
||||||
|
mode = Mode.INTERPRET;
|
||||||
|
lastWord.visible(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private LogicOperand lOp(Obj obj) {
|
||||||
|
try {
|
||||||
|
return (LogicOperand) obj;
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
throw new TypeMismatched(obj + " cannot do logic operators on " + obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArithmeticOperand aOp(Obj obj) {
|
||||||
|
try {
|
||||||
|
return (ArithmeticOperand) obj;
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
throw new TypeMismatched(obj + " cannot do arithmetic");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void show(Obj pop) {
|
||||||
|
transcript.show(pop.asStr().value());
|
||||||
|
transcript.cr();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void disassemble() {
|
||||||
|
String name = (String) stack.pop().value();
|
||||||
|
Word word = dict.at(name);
|
||||||
|
if (word instanceof ColonDef) {
|
||||||
|
int address = ((ColonDef)word).address;
|
||||||
|
while (true) {
|
||||||
|
transcript.show(String.format("[%08X] %s", address, heap[address]));
|
||||||
|
transcript.cr();
|
||||||
|
if (heap[address] instanceof Word && ((Word)heap[address]).name().equals(EXIT)
|
||||||
|
&& heap[address+1] == Nil.INSTANCE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
address++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
System.err.println("Not colon def: " + word);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sortDsc() {
|
||||||
|
stack.sortDsc();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sortAsc() {
|
||||||
|
stack.sortAsc();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reverse() {
|
||||||
|
stack.reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addPrimitive(String name, Runnable code) {
|
||||||
|
dict.add(new Primitive(name, code));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void eval(String source) {
|
||||||
|
eval(new StringReader(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void eval(Reader reader) {
|
||||||
|
this.reader = reader;
|
||||||
|
String token = word();
|
||||||
|
while (!token.isEmpty()) {
|
||||||
|
onTokenFound(token);
|
||||||
|
token = word();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compile a temporary word into the scratch area and call it.
|
||||||
|
* This is useful for evaluating words without interpretation semantics.
|
||||||
|
* Like: if else then, loops, quotations
|
||||||
|
*/
|
||||||
|
public void compileTmpAndEval(String script) {
|
||||||
|
int savedDp = dp;
|
||||||
|
Mode savedMode = mode;
|
||||||
|
try {
|
||||||
|
dp = heap.length - SCRATCH_SIZE;
|
||||||
|
mode = Mode.COMPILE;
|
||||||
|
eval(script);
|
||||||
|
eval(";");
|
||||||
|
mode = Mode.INTERPRET;
|
||||||
|
innerLoop(heap.length - SCRATCH_SIZE);
|
||||||
|
} finally {
|
||||||
|
dp = savedDp;
|
||||||
|
mode = savedMode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String word() {
|
||||||
|
StringBuilder token = new StringBuilder();
|
||||||
|
int key = key();
|
||||||
|
while (key != -1) {
|
||||||
|
char chr = (char) key;
|
||||||
|
if (Character.isWhitespace(chr)) {
|
||||||
|
if (token.length() > 0)
|
||||||
|
return token.toString();
|
||||||
|
token.setLength(0);
|
||||||
|
} else {
|
||||||
|
token.append(chr);
|
||||||
|
}
|
||||||
|
key = key();
|
||||||
|
}
|
||||||
|
return token.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int key() {
|
||||||
|
try {
|
||||||
|
return reader.read();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onTokenFound(String name) {
|
||||||
|
Word word = dict.at(name);
|
||||||
|
switch (mode) {
|
||||||
|
case INTERPRET:
|
||||||
|
if (word != null)
|
||||||
|
word.enter();
|
||||||
|
else
|
||||||
|
stack.push(recognize(name));
|
||||||
|
break;
|
||||||
|
case COMPILE:
|
||||||
|
if (word != null) {
|
||||||
|
if (dict.isImmediate(name))
|
||||||
|
word.enter();
|
||||||
|
else
|
||||||
|
heap[dp++] = word;
|
||||||
|
} else {
|
||||||
|
heap[dp++] = dict.at("lit");
|
||||||
|
heap[dp++] = recognize(name);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Obj recognize(String token) {
|
||||||
|
Obj str = recognizeStr(token);
|
||||||
|
if (str != null) return str;
|
||||||
|
return Num.parse(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Obj recognizeStr(String firstToken) {
|
||||||
|
if (!firstToken.startsWith("'")) return null;
|
||||||
|
StringBuilder str = new StringBuilder(firstToken.substring(1));
|
||||||
|
if (firstToken.endsWith("'") && firstToken.length() > 1) {
|
||||||
|
str.setLength(str.length() - 1);
|
||||||
|
} else {
|
||||||
|
str.append(" ");
|
||||||
|
int k = key();
|
||||||
|
while (k != -1 && (char) k != '\'') {
|
||||||
|
str.append((char) k);
|
||||||
|
k = key();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Str(str.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void innerLoop(int address) {
|
||||||
|
ip = address;
|
||||||
|
Word word = (Word) heap[ip++];
|
||||||
|
while (!EXIT.equals(word.name())) {
|
||||||
|
word.enter();
|
||||||
|
word = (Word) heap[ip++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Word get(String name) {
|
||||||
|
return dict.at(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Obj pop() {
|
||||||
|
return stack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int stackSize() {
|
||||||
|
return stack.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int rStackSize() {
|
||||||
|
return rstack.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void switchStack(FclStack stack) {
|
||||||
|
this.stack.switchStack(stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
mode = Mode.INTERPRET;
|
||||||
|
stack.clean();
|
||||||
|
rstack.clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> wordList() {
|
||||||
|
return dict.wordList();
|
||||||
|
}
|
||||||
|
}
|
108
FclStack.java
Normal file
108
FclStack.java
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
package com.vectron.fcl;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.vectron.forthcalc.domain.ports.FileStore;
|
||||||
|
import com.vectron.fcl.types.Obj;
|
||||||
|
import com.vectron.forthcalc.support.FclTypeAdapter;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
public class FclStack {
|
||||||
|
private static final Gson gson;
|
||||||
|
private final Stack<Obj> stack = new Stack<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
FclTypeAdapter typeAdapter = new FclTypeAdapter();
|
||||||
|
gson = new GsonBuilder()
|
||||||
|
.registerTypeAdapter(Obj.class, typeAdapter)
|
||||||
|
.setLenient()
|
||||||
|
.serializeSpecialFloatingPointValues()
|
||||||
|
.create();
|
||||||
|
typeAdapter.setGSon(gson);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void push(Obj obj) {
|
||||||
|
stack.push(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Obj pop() {
|
||||||
|
return stack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Obj peek() {
|
||||||
|
return stack.peek();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean empty() {
|
||||||
|
return stack.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clean() {
|
||||||
|
stack.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return stack.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Obj at(int index) {
|
||||||
|
return stack.get(stack.size() - index -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void switchStack(FclStack other) {
|
||||||
|
List<Obj> copy = new ArrayList<>(other.stack);
|
||||||
|
other.stack.clear();
|
||||||
|
other.stack.addAll(this.stack);
|
||||||
|
this.stack.clear();
|
||||||
|
this.stack.addAll(copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sortDsc() {
|
||||||
|
Collections.sort(stack, (o1, o2) -> o1.compareTo(o2));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sortAsc() {
|
||||||
|
Collections.sort(stack, (o1, o2) -> o2.compareTo(o1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reverse() {
|
||||||
|
Collections.reverse(stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void load(FileStore fileStore, String id) {
|
||||||
|
FileInputStream stream = null;
|
||||||
|
try {
|
||||||
|
stream = fileStore.open(fileName(id));
|
||||||
|
Obj[] loaded = gson.fromJson(new BufferedReader(new InputStreamReader(stream)), Obj[].class);
|
||||||
|
stack.clear();
|
||||||
|
for (Obj each : loaded)
|
||||||
|
stack.add(each);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
if (stream != null) {
|
||||||
|
try {
|
||||||
|
stream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save(FileStore fileStore, String id) {
|
||||||
|
fileStore.save(gson.toJson(stack.toArray(new Obj[0])).getBytes(), fileName(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String fileName(String id) {
|
||||||
|
return String.format("stack%s.json", id);
|
||||||
|
}
|
||||||
|
}
|
19
RamTranscript.java
Normal file
19
RamTranscript.java
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package com.vectron.fcl;
|
||||||
|
|
||||||
|
public class RamTranscript implements Transcript {
|
||||||
|
private final StringBuilder content = new StringBuilder();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void show(String str) {
|
||||||
|
content.append(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cr() {
|
||||||
|
content.append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String content() {
|
||||||
|
return content.toString();
|
||||||
|
}
|
||||||
|
}
|
17
Transcript.java
Normal file
17
Transcript.java
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package com.vectron.fcl;
|
||||||
|
|
||||||
|
public interface Transcript {
|
||||||
|
void show(String str);
|
||||||
|
void cr();
|
||||||
|
|
||||||
|
Transcript STDOUT = new Transcript() {
|
||||||
|
public void show(String str) {
|
||||||
|
System.out.print(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cr() {
|
||||||
|
System.out.println("");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
7
exceptions/Aborted.java
Normal file
7
exceptions/Aborted.java
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package com.vectron.fcl.exceptions;
|
||||||
|
|
||||||
|
public class Aborted extends FclException {
|
||||||
|
public Aborted(String str) {
|
||||||
|
super(str);
|
||||||
|
}
|
||||||
|
}
|
11
exceptions/FclException.java
Normal file
11
exceptions/FclException.java
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package com.vectron.fcl.exceptions;
|
||||||
|
|
||||||
|
public class FclException extends RuntimeException {
|
||||||
|
public FclException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FclException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
11
exceptions/InterOpFailed.java
Normal file
11
exceptions/InterOpFailed.java
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package com.vectron.fcl.exceptions;
|
||||||
|
|
||||||
|
public class InterOpFailed extends FclException {
|
||||||
|
public InterOpFailed(Exception e) {
|
||||||
|
super(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InterOpFailed(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
7
exceptions/NotUnderstood.java
Normal file
7
exceptions/NotUnderstood.java
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package com.vectron.fcl.exceptions;
|
||||||
|
|
||||||
|
public class NotUnderstood extends FclException {
|
||||||
|
public NotUnderstood(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
21
exceptions/TypeMismatched.java
Normal file
21
exceptions/TypeMismatched.java
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package com.vectron.fcl.exceptions;
|
||||||
|
|
||||||
|
import com.vectron.fcl.types.Obj;
|
||||||
|
|
||||||
|
public class TypeMismatched extends FclException {
|
||||||
|
public TypeMismatched(String operator, Obj a, Obj b) {
|
||||||
|
super(String.format("Unsupported types for %s: %s and %s", operator, a, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypeMismatched(String operator, Obj a) {
|
||||||
|
super(String.format("Unsupported types for %s: %s", operator, a));
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypeMismatched(Obj obj, String type) {
|
||||||
|
super(obj + " (" + obj.getClass().getSimpleName() + ") is not convertible to " + type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypeMismatched(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
50
interop/JvmInterOp.java
Normal file
50
interop/JvmInterOp.java
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
package com.vectron.fcl.interop;
|
||||||
|
|
||||||
|
import com.vectron.fcl.FclStack;
|
||||||
|
import com.vectron.fcl.exceptions.InterOpFailed;
|
||||||
|
import com.vectron.fcl.types.JvmObj;
|
||||||
|
import com.vectron.fcl.types.Obj;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import static com.vectron.fcl.interop.MethodSpec.processResult;
|
||||||
|
|
||||||
|
public class JvmInterOp {
|
||||||
|
private static final Random RND = new Random();
|
||||||
|
private final FclStack stack;
|
||||||
|
|
||||||
|
public JvmInterOp(FclStack stack) {
|
||||||
|
this.stack = stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void jvmCallStatic() {
|
||||||
|
MethodSpec spec = MethodSpec.parseStatic(stack.pop().asStr().value());
|
||||||
|
spec.invoke(stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void jvmCallMethod() {
|
||||||
|
String methodName = stack.pop().asStr().value();
|
||||||
|
Obj receiver = stack.pop();
|
||||||
|
MethodSpec spec = MethodSpec.parseDynamic(
|
||||||
|
methodName,
|
||||||
|
receiver instanceof JvmObj ? ((JvmObj) receiver).value() : receiver);
|
||||||
|
spec.invoke(stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void jvmStaticVar() {
|
||||||
|
String spec = stack.pop().asStr().value();
|
||||||
|
String[] parts = spec.split("/");
|
||||||
|
String className = parts[0];
|
||||||
|
String varName = parts[1];
|
||||||
|
try {
|
||||||
|
Class<?> clazz = Class.forName(className);
|
||||||
|
processResult(clazz.getDeclaredField(varName).get(null), stack);
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
throw new InterOpFailed(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double random() {
|
||||||
|
return RND.nextDouble();
|
||||||
|
}
|
||||||
|
}
|
123
interop/MethodSpec.java
Normal file
123
interop/MethodSpec.java
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
package com.vectron.fcl.interop;
|
||||||
|
|
||||||
|
import com.vectron.fcl.FclStack;
|
||||||
|
import com.vectron.fcl.exceptions.InterOpFailed;
|
||||||
|
import com.vectron.fcl.types.Bool;
|
||||||
|
import com.vectron.fcl.types.JvmObj;
|
||||||
|
import com.vectron.fcl.types.Nil;
|
||||||
|
import com.vectron.fcl.types.Num;
|
||||||
|
import com.vectron.fcl.types.Obj;
|
||||||
|
import com.vectron.fcl.types.Str;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
class MethodSpec {
|
||||||
|
private final Class<?> clazz;
|
||||||
|
private final Object receiver;
|
||||||
|
private final String methodName;
|
||||||
|
private final int arity;
|
||||||
|
private final String typeSpec;
|
||||||
|
|
||||||
|
public static MethodSpec parseStatic(String spec) {
|
||||||
|
String[] parts = spec.split("/");
|
||||||
|
try {
|
||||||
|
return new MethodSpec(Class.forName(parts[0]), null, parts[1], typeSpec(parts, 2));
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MethodSpec parseDynamic(String spec, Object receiver) {
|
||||||
|
String[] parts = spec.split("/");
|
||||||
|
return new MethodSpec(receiver.getClass(), receiver, parts[0], typeSpec(parts, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String typeSpec(String[] parts, int index) {
|
||||||
|
String types = "";
|
||||||
|
if (parts.length > index) {
|
||||||
|
types = parts[index];
|
||||||
|
}
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodSpec(Class<?> clazz, Object receiver, String methodName, String typeSpec) {
|
||||||
|
this.clazz = clazz;
|
||||||
|
this.receiver = receiver;
|
||||||
|
this.methodName = methodName;
|
||||||
|
this.arity = typeSpec.length();
|
||||||
|
this.typeSpec = typeSpec;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void invoke(FclStack stack) {
|
||||||
|
List<Object> params = new ArrayList<>();
|
||||||
|
List<Class<?>> types = new ArrayList<>();
|
||||||
|
for (int i = 0; i < arity; i++) {
|
||||||
|
Obj value = stack.pop();
|
||||||
|
Class<?> clazz = typeOf(typeSpec.charAt(i));
|
||||||
|
addParam(params, value, clazz);
|
||||||
|
types.add(clazz);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Method method = clazz.getMethod(methodName, types.toArray(new Class[0]));
|
||||||
|
method.setAccessible(true);
|
||||||
|
Object result = method.invoke(receiver, params.toArray(new Object[0]));
|
||||||
|
if (!method.getReturnType().getSimpleName().equals("void"))
|
||||||
|
processResult(result, stack);
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
throw new InterOpFailed(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void processResult(Object result, FclStack stack) {
|
||||||
|
if (result != null) {
|
||||||
|
if (result instanceof Number)
|
||||||
|
stack.push(new Num((Number) result));
|
||||||
|
else if (result instanceof String)
|
||||||
|
stack.push(new Str((String) result));
|
||||||
|
else if (result instanceof Boolean)
|
||||||
|
stack.push((boolean)result ? Bool.TRUE : Bool.FALSE);
|
||||||
|
else if (result instanceof Obj)
|
||||||
|
stack.push((Obj)result);
|
||||||
|
else
|
||||||
|
stack.push(new JvmObj(result));
|
||||||
|
} else {
|
||||||
|
stack.push(Nil.INSTANCE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addParam(List<Object> params, Obj value, Class<?> clazz) {
|
||||||
|
if (clazz == Integer.TYPE)
|
||||||
|
params.add(value.intValue());
|
||||||
|
else if (clazz == Long.TYPE)
|
||||||
|
params.add(value.longValue());
|
||||||
|
else if (clazz == Double.TYPE)
|
||||||
|
params.add(value.doubleValue());
|
||||||
|
else if (clazz == String.class)
|
||||||
|
params.add((String)value.value());
|
||||||
|
else if (clazz == Map.class)
|
||||||
|
params.add((Map)value.value());
|
||||||
|
else if (clazz == List.class)
|
||||||
|
params.add((List)value.value());
|
||||||
|
else if (clazz == Obj.class)
|
||||||
|
params.add(value);
|
||||||
|
else
|
||||||
|
throw new InterOpFailed("Unsupported inter-op type: " + clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class<?> typeOf(Character type) {
|
||||||
|
switch (type) {
|
||||||
|
case 'i': return Integer.TYPE;
|
||||||
|
case 'd': return Double.TYPE;
|
||||||
|
case 'l': return Long.TYPE;
|
||||||
|
case 's': return String.class;
|
||||||
|
case 'm': return Map.class;
|
||||||
|
case 't': return List.class;
|
||||||
|
case 'O': return Obj.class;
|
||||||
|
default:
|
||||||
|
throw new InterOpFailed("Invalid type spec: " + type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
types/ArithmeticOperand.java
Normal file
8
types/ArithmeticOperand.java
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
package com.vectron.fcl.types;
|
||||||
|
|
||||||
|
public interface ArithmeticOperand {
|
||||||
|
Obj add(Obj other);
|
||||||
|
Obj sub(Obj other);
|
||||||
|
Obj mul(Obj other);
|
||||||
|
Obj div(Obj other);
|
||||||
|
}
|
94
types/Bool.java
Normal file
94
types/Bool.java
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
package com.vectron.fcl.types;
|
||||||
|
|
||||||
|
import com.vectron.fcl.exceptions.TypeMismatched;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import static com.vectron.fcl.Fcl.STRICT;
|
||||||
|
|
||||||
|
public class Bool implements Obj, LogicOperand {
|
||||||
|
public static final Bool TRUE = new Bool(true);
|
||||||
|
public static final Bool FALSE = new Bool(false);
|
||||||
|
private final boolean value;
|
||||||
|
|
||||||
|
private Bool(boolean value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bool and(Obj other) {
|
||||||
|
return new Bool(value && other.boolValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bool or(Obj other) {
|
||||||
|
return new Bool(value || other.boolValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bool not() {
|
||||||
|
return new Bool(!value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean boolValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Num asNum() {
|
||||||
|
if (STRICT) throw new TypeMismatched(this, "num");
|
||||||
|
return value ? Num.ONE : Num.ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object value() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long longValue() {
|
||||||
|
if (STRICT) throw new TypeMismatched(this, "long");
|
||||||
|
return value ? 1l : 0l;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int intValue() {
|
||||||
|
if (STRICT) throw new TypeMismatched(this, "int");
|
||||||
|
return value ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double doubleValue() {
|
||||||
|
if (STRICT) throw new TypeMismatched(this, "double");
|
||||||
|
return value ? 1.0 : 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Str asStr() {
|
||||||
|
return new Str(toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return value ? "true" : "false";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
Bool bool = (Bool) o;
|
||||||
|
return value == bool.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Obj o) {
|
||||||
|
if (o instanceof Bool)
|
||||||
|
return Boolean.compare(value, o.boolValue());
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
124
types/Dic.java
Normal file
124
types/Dic.java
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
package com.vectron.fcl.types;
|
||||||
|
|
||||||
|
import com.vectron.fcl.exceptions.TypeMismatched;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static com.vectron.fcl.Fcl.STRICT;
|
||||||
|
|
||||||
|
public class Dic implements Obj {
|
||||||
|
private final Map<Obj, Obj> value = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
public static Dic empty() {
|
||||||
|
return new Dic();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long longValue() {
|
||||||
|
throw new TypeMismatched(this, "long");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int intValue() {
|
||||||
|
throw new TypeMismatched(this, "int");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double doubleValue() {
|
||||||
|
throw new TypeMismatched(this, "double");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean boolValue() {
|
||||||
|
throw new TypeMismatched(this, "bool");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Num asNum() {
|
||||||
|
if (STRICT) throw new TypeMismatched(this, "num");
|
||||||
|
return Num.NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Str asStr() {
|
||||||
|
return new Str(toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object value() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Obj o) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterator<Lst> iterator() {
|
||||||
|
return new Iterator<Lst>() {
|
||||||
|
private Iterator<Map.Entry<Obj,Obj>> it = value.entrySet().iterator();
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return it.hasNext();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public Lst next() {
|
||||||
|
Map.Entry<Obj, Obj> next = it.next();
|
||||||
|
Lst result = Lst.empty();
|
||||||
|
result.append(next.getKey());
|
||||||
|
result.append(next.getValue());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return value.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void put(Obj key, Obj value) {
|
||||||
|
this.value.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Obj at(Obj key) {
|
||||||
|
return value.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(Obj item) {
|
||||||
|
value.remove(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Lst keys() {
|
||||||
|
Lst result = Lst.empty();
|
||||||
|
for (Obj each : value.keySet())
|
||||||
|
result.append(each);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Lst values() {
|
||||||
|
Lst result = Lst.empty();
|
||||||
|
for (Obj each : value.values())
|
||||||
|
result.append(each);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
value.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder result = new StringBuilder("#[ ");
|
||||||
|
int i = 0;
|
||||||
|
for (Map.Entry<Obj, Obj> each : value.entrySet()) {
|
||||||
|
result.append(each.getKey());
|
||||||
|
result.append(" ");
|
||||||
|
result.append(each.getValue());
|
||||||
|
if (i < value.size() -1) result.append(" "); i++;
|
||||||
|
}
|
||||||
|
result.append(" ]#");
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
}
|
69
types/JvmObj.java
Normal file
69
types/JvmObj.java
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
package com.vectron.fcl.types;
|
||||||
|
|
||||||
|
import com.vectron.fcl.exceptions.TypeMismatched;
|
||||||
|
|
||||||
|
import static com.vectron.fcl.Fcl.STRICT;
|
||||||
|
|
||||||
|
public class JvmObj implements Obj {
|
||||||
|
private final Object object;
|
||||||
|
|
||||||
|
public JvmObj(Object object) {
|
||||||
|
this.object = object;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long longValue() {
|
||||||
|
if (object instanceof Number)
|
||||||
|
return ((Number)object).longValue();
|
||||||
|
throw new TypeMismatched(this, "long");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int intValue() {
|
||||||
|
if (object instanceof Number)
|
||||||
|
return ((Number)object).intValue();
|
||||||
|
throw new TypeMismatched(this, "int");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double doubleValue() {
|
||||||
|
if (object instanceof Number)
|
||||||
|
return ((Number)object).doubleValue();
|
||||||
|
throw new TypeMismatched(this, "double");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean boolValue() {
|
||||||
|
if (object instanceof Boolean)
|
||||||
|
return (boolean) object;
|
||||||
|
throw new TypeMismatched(this, "bool");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Num asNum() {
|
||||||
|
if (object instanceof Number)
|
||||||
|
return new Num((Number)object);
|
||||||
|
if (STRICT) throw new TypeMismatched(this, "num");
|
||||||
|
return Num.NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Str asStr() {
|
||||||
|
return new Str(toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object value() {
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Obj o) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "JvmObj:" + object;
|
||||||
|
}
|
||||||
|
}
|
7
types/LogicOperand.java
Normal file
7
types/LogicOperand.java
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package com.vectron.fcl.types;
|
||||||
|
|
||||||
|
public interface LogicOperand {
|
||||||
|
Obj and(Obj other);
|
||||||
|
Obj or(Obj other);
|
||||||
|
Obj not();
|
||||||
|
}
|
174
types/Lst.java
Normal file
174
types/Lst.java
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
package com.vectron.fcl.types;
|
||||||
|
|
||||||
|
import com.vectron.fcl.exceptions.TypeMismatched;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.vectron.fcl.Fcl.STRICT;
|
||||||
|
|
||||||
|
public class Lst implements Obj, ArithmeticOperand {
|
||||||
|
private final List<Obj> value = new ArrayList<>();
|
||||||
|
|
||||||
|
public static Lst empty() {
|
||||||
|
return new Lst();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long longValue() {
|
||||||
|
throw new TypeMismatched(this, "long");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int intValue() {
|
||||||
|
throw new TypeMismatched(this, "int");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double doubleValue() {
|
||||||
|
throw new TypeMismatched(this, "double");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean boolValue() {
|
||||||
|
throw new TypeMismatched(this, "bool");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Num asNum() {
|
||||||
|
if (STRICT) throw new TypeMismatched(this, "num");
|
||||||
|
return Num.NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object value() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Obj o) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return value.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void append(Obj value) {
|
||||||
|
this.value.add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void prep(Obj value) {
|
||||||
|
this.value.add(0, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Obj at(Obj index) {
|
||||||
|
return value.get(index.intValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public int indexOf(Obj item) {
|
||||||
|
return value.indexOf(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Lst concat(Obj other) {
|
||||||
|
Lst result = Lst.empty();
|
||||||
|
result.value.addAll(value);
|
||||||
|
result.value.addAll((List)other.value());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Lst subList(int start, int stop) {
|
||||||
|
Lst result = Lst.empty();
|
||||||
|
result.value.addAll(value.subList(start, stop));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(Obj item) {
|
||||||
|
value.remove(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeAt(int index) {
|
||||||
|
value.remove(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
value.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterator<Obj> iterator() {
|
||||||
|
return value.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Lst reverse() {
|
||||||
|
Lst result = Lst.empty();
|
||||||
|
for (int i = value.size() - 1; i >= 0; i--)
|
||||||
|
result.append(value.get(i));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Str asStr() {
|
||||||
|
return new Str(toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder result = new StringBuilder("[ ");
|
||||||
|
for (int i = 0; i < value.size(); i++) {
|
||||||
|
Obj each = value.get(i);
|
||||||
|
result.append(each);
|
||||||
|
if (i < value.size() -1) result.append(" ");
|
||||||
|
}
|
||||||
|
result.append(" ]");
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Lst add(Obj other) {
|
||||||
|
if (other instanceof Num) {
|
||||||
|
Lst result = Lst.empty();
|
||||||
|
for (Obj each : value)
|
||||||
|
result.append(each.asNum().add(other));
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
throw new TypeMismatched("+", this, other);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Lst sub(Obj other) {
|
||||||
|
if (other instanceof Num) {
|
||||||
|
Lst result = Lst.empty();
|
||||||
|
for (Obj each : value)
|
||||||
|
result.append(each.asNum().sub(other));
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
throw new TypeMismatched("-", this, other);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Lst mul(Obj other) {
|
||||||
|
if (other instanceof Num) {
|
||||||
|
Lst result = Lst.empty();
|
||||||
|
for (Obj each : value)
|
||||||
|
result.append(each.asNum().mul(other));
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
throw new TypeMismatched("*", this, other);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Lst div(Obj other) {
|
||||||
|
if (other instanceof Num) {
|
||||||
|
Lst result = Lst.empty();
|
||||||
|
for (Obj each : value)
|
||||||
|
result.append(each.asNum().div(other));
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
throw new TypeMismatched("/", this, other);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
61
types/Nil.java
Normal file
61
types/Nil.java
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
package com.vectron.fcl.types;
|
||||||
|
|
||||||
|
import com.vectron.fcl.exceptions.TypeMismatched;
|
||||||
|
|
||||||
|
import static com.vectron.fcl.Fcl.STRICT;
|
||||||
|
|
||||||
|
public class Nil implements Obj {
|
||||||
|
public static final Nil INSTANCE = new Nil();
|
||||||
|
|
||||||
|
private Nil() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long longValue() {
|
||||||
|
if (STRICT) throw new TypeMismatched(this, "long");
|
||||||
|
return 0l;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int intValue() {
|
||||||
|
if (STRICT) throw new TypeMismatched(this, "int");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double doubleValue() {
|
||||||
|
if (STRICT) throw new TypeMismatched(this, "double");
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean boolValue() {
|
||||||
|
if (STRICT) throw new TypeMismatched(this, "bool");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Num asNum() {
|
||||||
|
if (STRICT) throw new TypeMismatched(this, "num");
|
||||||
|
return Num.ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Str asStr() {
|
||||||
|
return new Str(toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object value() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Obj o) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "nil";
|
||||||
|
}
|
||||||
|
}
|
260
types/Num.java
Normal file
260
types/Num.java
Normal file
|
@ -0,0 +1,260 @@
|
||||||
|
package com.vectron.fcl.types;
|
||||||
|
|
||||||
|
import com.vectron.fcl.exceptions.NotUnderstood;
|
||||||
|
import com.vectron.fcl.exceptions.TypeMismatched;
|
||||||
|
|
||||||
|
import java.text.NumberFormat;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import static com.vectron.fcl.Fcl.STRICT;
|
||||||
|
|
||||||
|
public class Num implements Obj, LogicOperand, ArithmeticOperand {
|
||||||
|
public static final Num ZERO = new Num(0);
|
||||||
|
public static final Num ONE = new Num(1);
|
||||||
|
public static final Num NAN = new Num(Double.NaN);
|
||||||
|
private static final NumberFormat format = NumberFormat.getNumberInstance();
|
||||||
|
private final Number value;
|
||||||
|
|
||||||
|
static {
|
||||||
|
format.setMaximumFractionDigits(4);
|
||||||
|
format.setGroupingUsed(false);
|
||||||
|
format.setParseIntegerOnly(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Num(Number value) {
|
||||||
|
if (value instanceof Integer)
|
||||||
|
this.value = ((Integer)value).longValue();
|
||||||
|
else
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Num parse(String str) {
|
||||||
|
try {
|
||||||
|
return new Num(Long.parseLong(str));
|
||||||
|
} catch (NumberFormatException e1) {
|
||||||
|
try {
|
||||||
|
return new Num(Double.parseDouble(str));
|
||||||
|
} catch (NumberFormatException e2) {
|
||||||
|
throw new NotUnderstood("Undefined word: " + str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (value instanceof Long) {
|
||||||
|
return Long.toString((Long) value);
|
||||||
|
} else if (value instanceof Double) {
|
||||||
|
return format.format(value);
|
||||||
|
}
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Obj add(Obj other) {
|
||||||
|
if (value instanceof Long && other.value() instanceof Long)
|
||||||
|
return new Num((Long) value + (Long)other.value());
|
||||||
|
else if (value instanceof Long && other.value() instanceof Double)
|
||||||
|
return new Num((Long) value + (Double)other.value());
|
||||||
|
else if (value instanceof Double && other.value() instanceof Long)
|
||||||
|
return new Num((Double) value + (Long)other.value());
|
||||||
|
else if (value instanceof Double && other.value() instanceof Double)
|
||||||
|
return new Num((Double) value + (Double) other.value());
|
||||||
|
else if (other instanceof Lst)
|
||||||
|
return ((Lst) other).add(this);
|
||||||
|
else if (STRICT)
|
||||||
|
throw new TypeMismatched("+", this, other);
|
||||||
|
return Num.NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Obj sub(Obj other) {
|
||||||
|
if (value instanceof Long && other.value() instanceof Long)
|
||||||
|
return new Num((Long) value - (Long)other.value());
|
||||||
|
else if (value instanceof Long && other.value() instanceof Double)
|
||||||
|
return new Num((Long) value - (Double)other.value());
|
||||||
|
else if (value instanceof Double && other.value() instanceof Long)
|
||||||
|
return new Num((Double) value - (Long)other.value());
|
||||||
|
else if (value instanceof Double && other.value() instanceof Double)
|
||||||
|
return new Num((Double) value - (Double) other.value());
|
||||||
|
else if (STRICT)
|
||||||
|
throw new TypeMismatched("-", this, other);
|
||||||
|
return Num.NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Obj mul(Obj other) {
|
||||||
|
if (value instanceof Long && other.value() instanceof Long)
|
||||||
|
return new Num((Long) value * (Long)other.value());
|
||||||
|
else if (value instanceof Long && other.value() instanceof Double)
|
||||||
|
return new Num((Long) value * (Double)other.value());
|
||||||
|
else if (value instanceof Double && other.value() instanceof Long)
|
||||||
|
return new Num((Double) value * (Long)other.value());
|
||||||
|
else if (value instanceof Double && other.value() instanceof Double)
|
||||||
|
return new Num((Double) value * (Double) other.value());
|
||||||
|
else if (other instanceof Lst)
|
||||||
|
return ((Lst) other).mul(this);
|
||||||
|
else if (other instanceof Str)
|
||||||
|
return ((Str) other).mul(this);
|
||||||
|
else if (STRICT)
|
||||||
|
throw new TypeMismatched("*", this, other);
|
||||||
|
return Num.NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Obj div(Obj other) {
|
||||||
|
if (value instanceof Long && other.value() instanceof Long)
|
||||||
|
return new Num(((Long) value).doubleValue() / (Long) other.value());
|
||||||
|
else if (value instanceof Long && other.value() instanceof Double)
|
||||||
|
return new Num((Long) value / (Double)other.value());
|
||||||
|
else if (value instanceof Double && other.value() instanceof Long)
|
||||||
|
return new Num((Double) value / (Long)other.value());
|
||||||
|
else if (value instanceof Double && other.value() instanceof Double)
|
||||||
|
return new Num((Double) value / (Double) other.value());
|
||||||
|
else if (STRICT)
|
||||||
|
throw new TypeMismatched("/", this, other);
|
||||||
|
return Num.NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Num power(Num exponent) {
|
||||||
|
if (value instanceof Long && exponent.value instanceof Long)
|
||||||
|
return new Num(Math.pow(((Long) value).doubleValue(), ((Long) exponent.value).doubleValue()));
|
||||||
|
else if (value instanceof Long && exponent.value instanceof Double)
|
||||||
|
return new Num(Math.pow(((Long) value).doubleValue(), exponent.doubleValue()));
|
||||||
|
else if (value instanceof Double && exponent.value instanceof Long)
|
||||||
|
return new Num(Math.pow((Double)value, ((Long) exponent.value).doubleValue()));
|
||||||
|
else if (value instanceof Double && exponent.value instanceof Double)
|
||||||
|
return new Num(Math.pow((Double)value, exponent.doubleValue()));
|
||||||
|
else if (STRICT)
|
||||||
|
throw new TypeMismatched("POW", this, exponent);
|
||||||
|
return Num.NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Num mod(Num other) {
|
||||||
|
try {
|
||||||
|
return new Num(this.longValue() % other.longValue());
|
||||||
|
} catch (TypeMismatched e) {
|
||||||
|
if (STRICT) throw e;
|
||||||
|
return Num.NAN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Num intDiv(Num other) {
|
||||||
|
try {
|
||||||
|
return new Num(this.longValue() / other.longValue());
|
||||||
|
} catch (TypeMismatched e) {
|
||||||
|
if (STRICT) throw e;
|
||||||
|
return Num.NAN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Num round() {
|
||||||
|
if (value instanceof Long)
|
||||||
|
return this;
|
||||||
|
else if (value instanceof Double)
|
||||||
|
return new Num(Math.round(doubleValue()));
|
||||||
|
else if (STRICT)
|
||||||
|
throw new TypeMismatched("ROUND", this);
|
||||||
|
return Num.NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Num and(Obj other) {
|
||||||
|
if (value instanceof Long && other.value() instanceof Long)
|
||||||
|
return new Num((Long)value & other.longValue());
|
||||||
|
else if (STRICT)
|
||||||
|
throw new TypeMismatched("AND", this, other);
|
||||||
|
return Num.NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Num or(Obj other) {
|
||||||
|
if (value instanceof Long && other.value() instanceof Long)
|
||||||
|
return new Num((Long)value | other.longValue());
|
||||||
|
else if (STRICT)
|
||||||
|
throw new TypeMismatched("OR", this, other);
|
||||||
|
return Num.NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Num not() {
|
||||||
|
if (value instanceof Long)
|
||||||
|
return new Num(~(Long)value );
|
||||||
|
else if (STRICT)
|
||||||
|
throw new TypeMismatched("Unsupported types for NOT operator: " + value.getClass());
|
||||||
|
return Num.NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bool greater(Num other) {
|
||||||
|
if (value instanceof Long && other.value instanceof Long)
|
||||||
|
return (Long) value > (Long)other.value ? Bool.TRUE : Bool.FALSE;
|
||||||
|
else if (value instanceof Long && other.value instanceof Double)
|
||||||
|
return (Long) value > (Double)other.value ? Bool.TRUE : Bool.FALSE;
|
||||||
|
else if (value instanceof Double && other.value instanceof Long)
|
||||||
|
return (Double) value > (Long)other.value ? Bool.TRUE : Bool.FALSE;
|
||||||
|
else if (value instanceof Double && other.value instanceof Double)
|
||||||
|
return (Double) value > (Double) other.value ? Bool.TRUE : Bool.FALSE;
|
||||||
|
else
|
||||||
|
throw new TypeMismatched("<", this, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
Num num = (Num) o;
|
||||||
|
return value.equals(num.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long longValue() {
|
||||||
|
if (value instanceof Long)
|
||||||
|
return (Long) value;
|
||||||
|
else if (value instanceof Double)
|
||||||
|
return Math.round((Double) value);
|
||||||
|
else
|
||||||
|
throw new TypeMismatched(this, "long");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int intValue() {
|
||||||
|
return ((Number)value).intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean boolValue() {
|
||||||
|
throw new TypeMismatched(this, "bool");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Num asNum() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Str asStr() {
|
||||||
|
return new Str(toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double doubleValue() {
|
||||||
|
return ((Number)value).doubleValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object value() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Obj other) {
|
||||||
|
return other instanceof Num
|
||||||
|
? Double.compare(doubleValue(), ((Num) other).doubleValue())
|
||||||
|
: -1;
|
||||||
|
}
|
||||||
|
}
|
11
types/Obj.java
Normal file
11
types/Obj.java
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package com.vectron.fcl.types;
|
||||||
|
|
||||||
|
public interface Obj extends Comparable<Obj> {
|
||||||
|
long longValue();
|
||||||
|
int intValue();
|
||||||
|
double doubleValue();
|
||||||
|
boolean boolValue();
|
||||||
|
Num asNum();
|
||||||
|
Str asStr();
|
||||||
|
Object value();
|
||||||
|
}
|
84
types/Primitive.java
Normal file
84
types/Primitive.java
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
package com.vectron.fcl.types;
|
||||||
|
|
||||||
|
import com.vectron.fcl.exceptions.TypeMismatched;
|
||||||
|
|
||||||
|
import static com.vectron.fcl.Fcl.STRICT;
|
||||||
|
|
||||||
|
public class Primitive implements Word {
|
||||||
|
private final Runnable code;
|
||||||
|
private final String name;
|
||||||
|
private boolean visible = true;
|
||||||
|
|
||||||
|
public Primitive(String name, Runnable code) {
|
||||||
|
this.code = code;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visible(boolean isVisible) {
|
||||||
|
this.visible = isVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean visible() {
|
||||||
|
return visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enter() {
|
||||||
|
code.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String name() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long longValue() {
|
||||||
|
throw new TypeMismatched(this,"long");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int intValue() {
|
||||||
|
throw new TypeMismatched(this,"int");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double doubleValue() {
|
||||||
|
throw new TypeMismatched(this, "double");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean boolValue() {
|
||||||
|
throw new TypeMismatched(this, "bool");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Num asNum() {
|
||||||
|
if (STRICT) throw new TypeMismatched(this, "num");
|
||||||
|
return Num.NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Str asStr() {
|
||||||
|
return new Str(toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object value() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "xt_" + name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Obj other) {
|
||||||
|
return other instanceof Primitive
|
||||||
|
? name.compareTo(((Primitive) other).name)
|
||||||
|
: -1;
|
||||||
|
}
|
||||||
|
}
|
73
types/Quot.java
Normal file
73
types/Quot.java
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
package com.vectron.fcl.types;
|
||||||
|
|
||||||
|
import com.vectron.fcl.exceptions.TypeMismatched;
|
||||||
|
|
||||||
|
import static com.vectron.fcl.Fcl.STRICT;
|
||||||
|
|
||||||
|
public class Quot implements Obj {
|
||||||
|
private final int address;
|
||||||
|
private final int stackFrame;
|
||||||
|
|
||||||
|
public static Quot create(int stackFrame, int address) {
|
||||||
|
return new Quot(stackFrame, address);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Quot(int stackFrame, int address) {
|
||||||
|
this.address = address;
|
||||||
|
this.stackFrame = stackFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Num address() {
|
||||||
|
return new Num(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Num stackFrame() {
|
||||||
|
return new Num(stackFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long longValue() {
|
||||||
|
throw new TypeMismatched(this, "long");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int intValue() {
|
||||||
|
throw new TypeMismatched(this, "int");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double doubleValue() {
|
||||||
|
throw new TypeMismatched(this, "double");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean boolValue() {
|
||||||
|
throw new TypeMismatched(this, "bool");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object value() {
|
||||||
|
throw new TypeMismatched(this, "value");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Num asNum() {
|
||||||
|
if (STRICT) throw new TypeMismatched(this, "num");
|
||||||
|
return Num.NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Str asStr() {
|
||||||
|
return new Str(toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Obj o) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Quotation: " + address + ", " + stackFrame;
|
||||||
|
}
|
||||||
|
}
|
97
types/Range.java
Normal file
97
types/Range.java
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
package com.vectron.fcl.types;
|
||||||
|
|
||||||
|
import com.vectron.fcl.exceptions.InterOpFailed;
|
||||||
|
import com.vectron.fcl.exceptions.TypeMismatched;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import static com.vectron.fcl.Fcl.STRICT;
|
||||||
|
|
||||||
|
public class Range implements Obj {
|
||||||
|
private RangeIterator iterator;
|
||||||
|
private final int from;
|
||||||
|
private final int to;
|
||||||
|
private final int by;
|
||||||
|
private int current;
|
||||||
|
|
||||||
|
public static Range create(int by, int to, int from) {
|
||||||
|
return new Range(from, to, by);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Range(int from, int to, int by) {
|
||||||
|
if (by == 0)
|
||||||
|
throw new InterOpFailed("Invalid increment for range: " + by);
|
||||||
|
this.from = from;
|
||||||
|
this.to = to;
|
||||||
|
this.by = by;
|
||||||
|
this.current = from;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long longValue() {
|
||||||
|
throw new TypeMismatched(this, "long");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int intValue() {
|
||||||
|
throw new TypeMismatched(this, "int");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double doubleValue() {
|
||||||
|
throw new TypeMismatched(this, "double");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean boolValue() {
|
||||||
|
throw new TypeMismatched(this, "bool");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Num asNum() {
|
||||||
|
if (STRICT) throw new TypeMismatched(this, "num");
|
||||||
|
return Num.NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Str asStr() {
|
||||||
|
return new Str(toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterator<Obj> iterator() {
|
||||||
|
if (iterator == null)
|
||||||
|
iterator = new RangeIterator();
|
||||||
|
return iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return by == 1
|
||||||
|
? String.format("%d..%d (%d)", from, to, current)
|
||||||
|
: String.format("%d...%d (%d) by %d", from, to, current, by);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object value() {
|
||||||
|
return iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Obj o) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RangeIterator implements Iterator<Obj> {
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return by > 0 ? current <= to : current >= to;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Obj next() {
|
||||||
|
Num result = new Num(current);
|
||||||
|
current += by;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
175
types/Str.java
Normal file
175
types/Str.java
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
package com.vectron.fcl.types;
|
||||||
|
|
||||||
|
import com.vectron.fcl.exceptions.TypeMismatched;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import static com.vectron.fcl.Fcl.STRICT;
|
||||||
|
|
||||||
|
public class Str implements Obj, ArithmeticOperand {
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
public Str(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long longValue() {
|
||||||
|
throw new TypeMismatched(this, "long");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int intValue() {
|
||||||
|
throw new TypeMismatched(this, "int");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double doubleValue() {
|
||||||
|
throw new TypeMismatched(this, "double");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean boolValue() {
|
||||||
|
throw new TypeMismatched(this, "bool");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String value() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Num asNum() {
|
||||||
|
if (STRICT) throw new TypeMismatched(this, "num");
|
||||||
|
return Num.parse(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Str asStr() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "'" + value + "'";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Str substr(int from, int to) {
|
||||||
|
return new Str(value.substring(from, to));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return value.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Str at(Obj index) {
|
||||||
|
return new Str(Character.toString(value.charAt(index.intValue())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Lst split(String delimiter) {
|
||||||
|
Lst result = Lst.empty();
|
||||||
|
for (String each : value.split(delimiter))
|
||||||
|
result.append(new Str(each));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Str upper() {
|
||||||
|
return new Str(value.toUpperCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Str lower() {
|
||||||
|
return new Str(value.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Str trim() {
|
||||||
|
return new Str(value.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
public int indexOf(Obj s) {
|
||||||
|
return value.indexOf((String)s.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Str replace(String olds, String news) {
|
||||||
|
return new Str(value.replaceAll(olds, news));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Str concat(Obj str) {
|
||||||
|
return new Str(value + (String)str.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Str reverse() {
|
||||||
|
return new Str(new StringBuilder(value).reverse().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterator<Str> iterator() {
|
||||||
|
return new Iterator<Str>() {
|
||||||
|
private int index = 0;
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return index < value.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Str next() {
|
||||||
|
return new Str(Character.toString(value.charAt(index++)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
Str str = (Str) o;
|
||||||
|
return value.equals(str.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Obj other) {
|
||||||
|
return other instanceof Str
|
||||||
|
? value.compareTo(((Str) other).value)
|
||||||
|
: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Str format(List<Obj> params) {
|
||||||
|
Object[] a = new Object[params.size()];
|
||||||
|
for (int i = 0; i < params.size(); i++) {
|
||||||
|
a[i] = params.get(i).value();
|
||||||
|
}
|
||||||
|
return new Str(String.format(value, a));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Obj add(Obj other) {
|
||||||
|
throw new TypeMismatched("+", this, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Obj sub(Obj other) {
|
||||||
|
throw new TypeMismatched("+", this, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Obj mul(Obj other) {
|
||||||
|
if (other instanceof Num) {
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
for (int i = 0; i < other.intValue(); i++) {
|
||||||
|
result.append(value);
|
||||||
|
}
|
||||||
|
return new Str(result.toString());
|
||||||
|
}
|
||||||
|
throw new TypeMismatched("*", this, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Obj div(Obj other) {
|
||||||
|
throw new TypeMismatched("+", this, other);
|
||||||
|
}
|
||||||
|
}
|
8
types/Word.java
Normal file
8
types/Word.java
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
package com.vectron.fcl.types;
|
||||||
|
|
||||||
|
public interface Word extends Obj {
|
||||||
|
void enter();
|
||||||
|
String name();
|
||||||
|
void visible(boolean isVisible);
|
||||||
|
boolean visible();
|
||||||
|
}
|
Loading…
Reference in a new issue