diff --git a/src/main/java/com/vectron/fcl/Fcl.java b/src/main/java/com/vectron/fcl/Fcl.java index f7101a2..f69fdb9 100644 --- a/src/main/java/com/vectron/fcl/Fcl.java +++ b/src/main/java/com/vectron/fcl/Fcl.java @@ -11,6 +11,7 @@ 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.Symbol; import com.vectron.fcl.types.Word; import java.io.IOException; @@ -18,7 +19,9 @@ import java.io.Reader; import java.io.StringReader; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; public class Fcl { @@ -35,6 +38,7 @@ public class Fcl { private Reader reader; private Mode mode = Mode.INTERPRET; private final Object[] heap; + private final Map symbols = new HashMap<>(); private int dp = SCRATCH_SIZE; private int ip = 0; private boolean trace = false; @@ -567,13 +571,15 @@ public class Fcl { private Obj recognize(String token) { Obj str = recognizeStr(token); if (str != null) return str; + Obj symbol = recognizeSymbol(token); + if (symbol != null) return symbol; 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) { + private Obj recognizeStr(String token) { + if (!token.startsWith("'")) return null; + StringBuilder str = new StringBuilder(token.substring(1)); + if (token.endsWith("'") && token.length() > 1) { str.setLength(str.length() - 1); } else { str.append(" "); @@ -586,6 +592,18 @@ public class Fcl { return new Str(str.toString()); } + private Obj recognizeSymbol(String token) { + return token.startsWith(":") && token.length() > 1 + ? symbol(token.substring(1)) + : null; + } + + private Symbol symbol(String name) { + if (!symbols.containsKey(name)) + symbols.put(name, new Symbol(name)); + return symbols.get(name); + } + private void innerLoop(int address) { ip = address; Word word = (Word) heap[ip++]; diff --git a/src/main/java/com/vectron/fcl/FclTypeAdapter.java b/src/main/java/com/vectron/fcl/FclTypeAdapter.java index 632f84b..85965a5 100644 --- a/src/main/java/com/vectron/fcl/FclTypeAdapter.java +++ b/src/main/java/com/vectron/fcl/FclTypeAdapter.java @@ -17,6 +17,7 @@ import com.vectron.fcl.types.Obj; import com.vectron.fcl.types.Quot; import com.vectron.fcl.types.Range; import com.vectron.fcl.types.Str; +import com.vectron.fcl.types.Symbol; import java.util.HashMap; import java.util.Map; @@ -32,6 +33,7 @@ public class FclTypeAdapter extends TypeAdapter { register("bool", Bool.class); register("str", Str.class); register("dic", Dic.class); + register("sym", Symbol.class); register("lst", Lst.class); register("nil", Nil.class); register("quot", Quot.class); diff --git a/src/main/java/com/vectron/fcl/types/Symbol.java b/src/main/java/com/vectron/fcl/types/Symbol.java new file mode 100644 index 0000000..3e2604b --- /dev/null +++ b/src/main/java/com/vectron/fcl/types/Symbol.java @@ -0,0 +1,63 @@ +package com.vectron.fcl.types; + +import com.vectron.fcl.exceptions.TypeMismatched; + +public class Symbol implements Obj { + private final String symbol; + + public Symbol(String symbol) { + this.symbol = symbol; + } + + @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() { + return null; + } + + @Override + public Str asStr() { + return new Str(symbol); + } + + @Override + public Object value() { + return symbol; + } + + @Override + public Object unwrap() { + return symbol; + } + + @Override + public String toString() { + return ":" + symbol; + } + + @Override + public int compareTo(Obj other) { + return other instanceof Symbol + ? symbol.compareTo(((Symbol) other).symbol) + : -1; + } +} diff --git a/src/test/java/com/vectron/fcl/FclTest.java b/src/test/java/com/vectron/fcl/FclTest.java index eed3f6d..a93e145 100644 --- a/src/test/java/com/vectron/fcl/FclTest.java +++ b/src/test/java/com/vectron/fcl/FclTest.java @@ -1187,6 +1187,16 @@ public class FclTest { evalPop("#[ 'headers' #[ 'Content-Type' 'text/plain' ]# 'content' #[ 'a' 1 ]# ]# +json-type").toString()); } + @Test + public void testSymbols() { + assertEquals(":my-symbol", evalPop(":my-symbol").toString()); + assertEquals(true, evalPop(":my-symbol :my-symbol =").boolValue()); + assertEquals(false, evalPop(":my-symbol :my-symbol !=").boolValue()); + assertEquals(false, evalPop(":my-symbol :my-symbol2 =").boolValue()); + assertEquals(true, evalPop(":my-symbol :my-symbol2 !=").boolValue()); + assertEquals(true, evalPop(": tst :my-symbol :my-symbol = ; tst").boolValue()); + } + private String transcript() { return transcript.content(); }