package moves

This commit is contained in:
zeroflag 2021-06-25 13:19:20 +02:00
parent 5b7f7c7562
commit 2c68ffa348
26 changed files with 2257 additions and 0 deletions

53
Dictionary.java Normal file
View 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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1,7 @@
package com.vectron.fcl.exceptions;
public class Aborted extends FclException {
public Aborted(String str) {
super(str);
}
}

View 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);
}
}

View 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);
}
}

View file

@ -0,0 +1,7 @@
package com.vectron.fcl.exceptions;
public class NotUnderstood extends FclException {
public NotUnderstood(String message) {
super(message);
}
}

View 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
View 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
View 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);
}
}
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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();
}