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