diff options
author | Rich Hickey <richhickey@gmail.com> | 2006-06-15 22:04:43 +0000 |
---|---|---|
committer | Rich Hickey <richhickey@gmail.com> | 2006-06-15 22:04:43 +0000 |
commit | 3d54fc288b133949a5f49a078641ca07e7ccf3d0 (patch) | |
tree | f1eca839a2d880ed42281075ecdd9a0e2f48cef4 | |
parent | a6945186568b8265f775e448778255d16daba8db (diff) |
reader in sync both platforms, symbols, vars, hostnames, numbers, strings, chars, lists, comments
-rw-r--r-- | src/cli/runtime/ClassName.cs | 24 | ||||
-rw-r--r-- | src/cli/runtime/InstanceMemberName.cs | 26 | ||||
-rw-r--r-- | src/cli/runtime/LineNumberingTextReader.cs | 20 | ||||
-rw-r--r-- | src/cli/runtime/LispReader.cs | 325 | ||||
-rw-r--r-- | src/cli/runtime/PersistentTree.cs | 2 | ||||
-rw-r--r-- | src/cli/runtime/RT.cs | 53 | ||||
-rw-r--r-- | src/cli/runtime/StaticMemberName.cs | 27 | ||||
-rw-r--r-- | src/cli/runtime/Symbol.cs | 2 | ||||
-rw-r--r-- | src/cli/runtime/Var.cs | 6 | ||||
-rw-r--r-- | src/jvm/clojure/lang/LispReader.java | 128 | ||||
-rw-r--r-- | src/jvm/clojure/lang/RT.java | 43 |
11 files changed, 626 insertions, 30 deletions
diff --git a/src/cli/runtime/ClassName.cs b/src/cli/runtime/ClassName.cs new file mode 100644 index 00000000..5d777974 --- /dev/null +++ b/src/cli/runtime/ClassName.cs @@ -0,0 +1,24 @@ +/**
+ * Copyright (c) Rich Hickey. All rights reserved.
+ * The use and distribution terms for this software are covered by the
+ * Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
+ * which can be found in the file CPL.TXT at the root of this distribution.
+ * By using this software in any fashion, you are agreeing to be bound by
+ * the terms of this license.
+ * You must not remove this notice, or any other, from this software.
+ **/
+
+using System;
+ +namespace clojure.lang
+{ +
+public class ClassName {
+readonly public String className;
+
+public ClassName(String className) {
+ this.className = className;
+}
+}
+
+}
diff --git a/src/cli/runtime/InstanceMemberName.cs b/src/cli/runtime/InstanceMemberName.cs new file mode 100644 index 00000000..594b44cb --- /dev/null +++ b/src/cli/runtime/InstanceMemberName.cs @@ -0,0 +1,26 @@ +/**
+ * Copyright (c) Rich Hickey. All rights reserved.
+ * The use and distribution terms for this software are covered by the
+ * Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
+ * which can be found in the file CPL.TXT at the root of this distribution.
+ * By using this software in any fashion, you are agreeing to be bound by
+ * the terms of this license.
+ * You must not remove this notice, or any other, from this software.
+ **/
+
+using System;
+ +namespace clojure.lang
+{ +
+public class InstanceMemberName {
+readonly public String className;
+readonly public String memberName;
+
+public InstanceMemberName(String className, String memberName) {
+ this.className = className;
+ this.memberName = memberName;
+}
+}
+
+}
diff --git a/src/cli/runtime/LineNumberingTextReader.cs b/src/cli/runtime/LineNumberingTextReader.cs index 7ccdc874..71f05e8d 100644 --- a/src/cli/runtime/LineNumberingTextReader.cs +++ b/src/cli/runtime/LineNumberingTextReader.cs @@ -19,6 +19,8 @@ public class LineNumberingTextReader : TextReader, IDisposable {
TextReader impl;
int line = 0;
+ int unreadChar;
+ bool haveUnread = false;
public LineNumberingTextReader(TextReader r){
this.impl = r;
@@ -29,11 +31,27 @@ public class LineNumberingTextReader : TextReader, IDisposable }
override public int Read(){
- int ret = impl.Read();
+ int ret;
+ if(haveUnread)
+ {
+ ret = unreadChar;
+ haveUnread = false;
+ }
+ else
+ ret = impl.Read();
if(ret == '\n')
++line;
return ret;
}
+
+ public void unread(int ch){
+ if(haveUnread)
+ throw new InvalidOperationException("Can't unread more than once in a row");
+ unreadChar = ch;
+ haveUnread = true;
+ if (ch == '\n')
+ --line;
+ }
override public int Peek(){
return impl.Peek();
diff --git a/src/cli/runtime/LispReader.cs b/src/cli/runtime/LispReader.cs new file mode 100644 index 00000000..9ccd21c7 --- /dev/null +++ b/src/cli/runtime/LispReader.cs @@ -0,0 +1,325 @@ +/**
+ * Copyright (c) Rich Hickey. All rights reserved.
+ * The use and distribution terms for this software are covered by the
+ * Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
+ * which can be found in the file CPL.TXT at the root of this distribution.
+ * By using this software in any fashion, you are agreeing to be bound by
+ * the terms of this license.
+ * You must not remove this notice, or any other, from this software.
+ **/
+
+using System;
+using System.IO;
+using System.Text.RegularExpressions;
+using java.math;
+using System.Text;
+using System.Collections;
+ +namespace clojure.lang
+{ +
+
+public class LispReader {
+
+static IFn[] macros = new IFn[256];
+static Regex symbolPat = new Regex("[:]?[\\D-[:\\.]][^:\\.]*",RegexOptions.Compiled);
+ static Regex varPat = new Regex("([\\D-[:\\.]][^:\\.]*):([\\D-[:\\.]][^:\\.]*)", RegexOptions.Compiled);
+ static Regex intPat = new Regex("[-+]?[0-9]+\\.?", RegexOptions.Compiled);
+ static Regex ratioPat = new Regex("([-+]?[0-9]+)/([0-9]+)", RegexOptions.Compiled);
+ static Regex floatPat = new Regex("[-+]?[0-9]+(\\.[0-9]+)?([eE][-+]?[0-9]+)?", RegexOptions.Compiled);
+
+ static Regex accessorPat = new Regex("\\.[a-zA-Z_]\\w*", RegexOptions.Compiled);
+ static Regex instanceMemberPat = new Regex("\\.([a-zA-Z_][\\w\\.]*)\\.([a-zA-Z_]\\w*)", RegexOptions.Compiled);
+ static Regex staticMemberPat = new Regex("([a-zA-Z_][\\w\\.]*)\\.([a-zA-Z_]\\w*)", RegexOptions.Compiled);
+ static Regex classNamePat = new Regex("([a-zA-Z_][\\w\\.]*)\\.", RegexOptions.Compiled);
+
+static LispReader(){
+macros['"'] = new StringReader();
+macros[';'] = new CommentReader();
+macros['('] = new ListReader();
+macros[')'] = new UnmatchedDelimiterReader();
+macros['\\'] = new CharacterReader();
+}
+
+static public Object read(LineNumberingTextReader r, bool eofIsError, Object eofValue, bool isRecursive)
+ {
+
+ for (; ;)
+ {
+ int ch = r.Read();
+
+ while (Char.IsWhiteSpace((char)ch))
+ ch = r.Read();
+
+ if (ch == -1)
+ {
+ if (eofIsError)
+ throw new Exception("EOF while reading");
+ return eofValue;
+ }
+
+ IFn macroFn = getMacro(ch);
+ if (macroFn != null)
+ {
+ Object ret = macroFn.invoke(r, (char)ch);
+ if(RT.suppressRead())
+ return null;
+ //no op macros return the reader
+ if (ret == r)
+ continue;
+ return ret;
+ }
+
+ String token = readToken(r,(char)ch);
+ if(RT.suppressRead())
+ return null;
+ return interpretToken(token);
+ }
+}
+
+static private String readToken(LineNumberingTextReader r, char initch) {
+ StringBuilder sb = new StringBuilder();
+ sb.Append(initch);
+
+ for(;;)
+ {
+ int ch = r.Read();
+ if(ch == -1 || Char.IsWhiteSpace((char)ch) || isMacro(ch))
+ {
+ r.unread(ch);
+ return sb.ToString();
+ }
+ sb.Append((char)ch);
+ }
+}
+
+static private Object interpretToken(String s) {
+ if (s.Equals("null"))
+ {
+ return null;
+ }
+ Object ret = null;
+ ret = matchSymbol(s);
+ if(ret != null)
+ return ret;
+ ret = matchVar(s);
+ if(ret != null)
+ return ret;
+ ret = matchNumber(s);
+ if(ret != null)
+ return ret;
+ ret = matchHostName(s);
+ if(ret != null)
+ return ret;
+
+
+ throw new InvalidDataException("Invalid syntax: " + s);
+}
+
+private static Object matchHostName(String s) {
+ Match m = accessorPat.Match(s);
+ if(m.Success && m.Length == s.Length)
+ return new Accessor(s);
+ m = classNamePat.Match(s);
+ if(m.Success && m.Length == s.Length)
+ return new ClassName(RT.resolveClassNameInContext(m.Groups[1].Value));
+ m = instanceMemberPat.Match(s);
+ if(m.Success && m.Length == s.Length)
+ return new InstanceMemberName(RT.resolveClassNameInContext(m.Groups[1].Value),m.Groups[2].Value);
+ m = staticMemberPat.Match(s);
+ if(m.Success && m.Length == s.Length)
+ return new StaticMemberName(RT.resolveClassNameInContext(m.Groups[1].Value),m.Groups[2].Value);
+
+ return null;
+}
+
+private static Object matchSymbol(String s) {
+ Match m = symbolPat.Match(s);
+ if(m.Success && m.Length == s.Length)
+ return Symbol.intern(s);
+ return null;
+}
+
+private static Object matchVar(String s) {
+ Match m = varPat.Match(s);
+ if(m.Success && m.Length == s.Length)
+ return Namespace.intern(m.Groups[1].Value,m.Groups[2].Value);
+ return null;
+}
+
+private static Object matchNumber(String s) {
+ Match m = intPat.Match(s);
+ if(m.Success && m.Length == s.Length)
+ return Num.from(new BigInteger(s));
+ m = floatPat.Match(s);
+ if(m.Success && m.Length == s.Length)
+ return Num.from(Double.Parse(s));
+ m = ratioPat.Match(s);
+ if(m.Success && m.Length == s.Length)
+ {
+ return Num.divide(new BigInteger(m.Groups[1].Value),new BigInteger(m.Groups[2].Value));
+ }
+ return null;
+}
+
+static private IFn getMacro(int ch) {
+ if (ch < macros.Length)
+ return macros[ch];
+ return null;
+}
+
+static private bool isMacro(int ch) {
+ return (ch < macros.Length && macros[ch] != null);
+}
+
+
+class StringReader : AFn{
+ override public Object invoke(Object reader, Object doublequote) {
+ StringBuilder sb = new StringBuilder();
+ LineNumberingTextReader r = (LineNumberingTextReader) reader;
+
+ for(int ch = r.Read();ch != '"';ch = r.Read())
+ {
+ if(ch == -1)
+ throw new Exception("EOF while reading string");
+ if(ch == '\\') //escape
+ {
+ ch = r.Read();
+ if(ch == -1)
+ throw new Exception("EOF while reading string");
+ switch(ch)
+ {
+ case 't':
+ ch = '\t';
+ break;
+ case 'r':
+ ch = '\r';
+ break;
+ case 'n':
+ ch = '\n';
+ break;
+ case '\\':
+ break;
+ case '"':
+ break;
+ default:
+ throw new Exception("Unsupported escape character: \\" + (char)ch);
+ }
+ }
+ sb.Append((char)ch);
+ }
+ return sb.ToString();
+ }
+
+}
+class CommentReader : AFn{
+ override public Object invoke(Object reader, Object semicolon)
+ {
+ LineNumberingTextReader r = (LineNumberingTextReader) reader;
+ int ch;
+ do
+ {
+ ch = r.Read();
+ } while (ch != -1 && ch != '\n' && ch != '\r');
+ return r;
+ }
+
+}
+class CharacterReader : AFn{
+ override public Object invoke(Object reader, Object backslash)
+ {
+ LineNumberingTextReader r = (LineNumberingTextReader) reader;
+ int ch = r.Read();
+ if(ch == -1)
+ throw new Exception("EOF while reading character");
+ String token = readToken(r,(char)ch);
+ if(token.Length == 1)
+ return token[0];
+ else if(token.Equals("newline"))
+ return '\n';
+ else if(token.Equals("space"))
+ return ' ';
+ else if(token.Equals("tab"))
+ return '\t';
+ throw new Exception("Unsupported character: \\" + token);
+ }
+
+}
+class ListReader : AFn{
+ override public Object invoke(Object reader, Object leftparen) {
+ LineNumberingTextReader r = (LineNumberingTextReader) reader;
+ return readDelimitedList(')', r, true);
+ }
+
+}
+class UnmatchedDelimiterReader : AFn{
+ override public Object invoke(Object reader, Object rightdelim) {
+ throw new Exception("Unmatched delimiter: " + rightdelim);
+ }
+
+}
+public static ISeq readDelimitedList(char delim, LineNumberingTextReader r, bool isRecursive) {
+ ArrayList a = new ArrayList();
+
+ for (; ;)
+ {
+ int ch = r.Read();
+
+ while (Char.IsWhiteSpace((char)ch))
+ ch = r.Read();
+
+ if (ch == -1)
+ throw new Exception("EOF while reading");
+
+ if(ch == delim)
+ break;
+
+ IFn macroFn = getMacro(ch);
+ if (macroFn != null)
+ {
+ Object mret = macroFn.invoke(r, (char)ch);
+ //no op macros return the reader
+ if (mret != r)
+ a.Add(mret);
+ }
+ else
+ {
+ r.unread(ch);
+
+ Object o = read(r, true, null, isRecursive);
+ if (o != r)
+ a.Add(o);
+ }
+ }
+
+ ISeq ret = null;
+ for(int i=a.Count-1;i>=0;--i)
+ ret = RT.cons(a[i], ret);
+ return ret;
+}
+
+public static void Main(String[] args){
+ LineNumberingTextReader r = new LineNumberingTextReader(Console.In);
+ TextWriter w = Console.Out;
+ Object ret = null;
+ try{
+ for(;;)
+ {
+ ret = LispReader.read(r, true, null, false);
+ RT.print(ret, w);
+ w.Write('\n');
+ w.Flush();
+ }
+ }
+ catch(Exception e)
+ {
+ //e.printStackTrace();
+ Console.Error.WriteLine(e.StackTrace);
+ }
+}
+
+
+}
+
+}
+
diff --git a/src/cli/runtime/PersistentTree.cs b/src/cli/runtime/PersistentTree.cs index 3b8d9bbc..b9335bdf 100644 --- a/src/cli/runtime/PersistentTree.cs +++ b/src/cli/runtime/PersistentTree.cs @@ -778,7 +778,7 @@ public void Reset() #endregion
} - //*
+ /*
[STAThread] static public void Main(String[] args){ if(args.Length != 1)
diff --git a/src/cli/runtime/RT.cs b/src/cli/runtime/RT.cs index b0d43116..36387c55 100644 --- a/src/cli/runtime/RT.cs +++ b/src/cli/runtime/RT.cs @@ -288,6 +288,59 @@ static public bool isLineNumberingReader(TextReader r) return r is LineNumberingTextReader;
}
+
+static public String resolveClassNameInContext(String className) {
+ //todo - look up in context var
+ return className;
+}
+
+static public bool suppressRead(){
+ //todo - look up in suppress-read var
+ return false;
+}
+
+static public void print(Object x, TextWriter w) {
+ //todo - make extensible
+ if(x == null)
+ w.Write("null");
+ else if(x is ISeq)
+ {
+ w.Write('(');
+ for(ISeq s = (ISeq)x;s != null;s = s.rest())
+ {
+ print(s.first(), w);
+ if(s.rest()!=null)
+ w.Write(' ');
+ }
+ w.Write(')');
+ }
+ else if(x is String)
+ {
+ w.Write('"');
+ w.Write(x.ToString());
+ w.Write('"');
+ }
+ else if(x is Char)
+ {
+ w.Write('\\');
+ char c = (char)x;
+ switch(c){
+ case '\n':
+ w.Write("newline");
+ break;
+ case '\t':
+ w.Write("tab");
+ break;
+ case ' ':
+ w.Write("space");
+ break;
+ default:
+ w.Write(c);
+ break;
+ }
+ }
+ else w.Write(x.ToString());
+}
/*-------------------------------- values --------------*/
static public Object setValues(params Object[] vals)
diff --git a/src/cli/runtime/StaticMemberName.cs b/src/cli/runtime/StaticMemberName.cs new file mode 100644 index 00000000..e1d9bdb9 --- /dev/null +++ b/src/cli/runtime/StaticMemberName.cs @@ -0,0 +1,27 @@ +/**
+ * Copyright (c) Rich Hickey. All rights reserved.
+ * The use and distribution terms for this software are covered by the
+ * Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
+ * which can be found in the file CPL.TXT at the root of this distribution.
+ * By using this software in any fashion, you are agreeing to be bound by
+ * the terms of this license.
+ * You must not remove this notice, or any other, from this software.
+ **/
+
+using System;
+ +namespace clojure.lang
+{ +
+public class StaticMemberName {
+readonly public String className;
+readonly public String memberName;
+
+public StaticMemberName(String className, String memberName) {
+ this.className = className;
+ this.memberName = memberName;
+}
+
+}
+
+}
diff --git a/src/cli/runtime/Symbol.cs b/src/cli/runtime/Symbol.cs index 78580b94..2ee91a91 100644 --- a/src/cli/runtime/Symbol.cs +++ b/src/cli/runtime/Symbol.cs @@ -25,7 +25,7 @@ static readonly Random rand = new Random(42); public readonly String name;
int hash = 0; -public String toString() +override public String ToString() { return name; } diff --git a/src/cli/runtime/Var.cs b/src/cli/runtime/Var.cs index d0b668eb..f62d8365 100644 --- a/src/cli/runtime/Var.cs +++ b/src/cli/runtime/Var.cs @@ -15,16 +15,16 @@ namespace clojure.lang {
public class Var : AFn
{
-public readonly Symbol sym;
public Namespace ns;
public Binding binding;
public IFn fn; //todo, bind to throw stub?
public IFn setfn;
volatile IPersistentMap threadBindings = PersistentArrayIdentityMap.EMPTY;
internal Var(Symbol sym, Namespace ns)
{
if(sym.GetType() != typeof(Symbol))
throw new ArgumentException("Only simple symbols can be vars");
this.ns = ns;
this.sym = sym;
}
public String toString()
{
if(ns == null)
return "#:" + sym;
return ns.name + ":" + sym;
}
public Var bind(Object val)
{
if(binding == null)
+public readonly Symbol sym;
public Namespace ns;
public Binding binding;
public IFn fn; //todo, bind to throw stub?
public IFn setfn;
volatile IPersistentMap threadBindings = PersistentArrayIdentityMap.EMPTY;
internal Var(Symbol sym, Namespace ns)
{
if(sym.GetType() != typeof(Symbol))
throw new ArgumentException("Only simple symbols can be vars");
this.ns = ns;
this.sym = sym;
}
override public String ToString()
{
if(ns == null)
return "#:" + sym;
return ns.name + ":" + sym;
}
public Var bind(Object val)
{
if(binding == null)
binding = new Binding(val);
else
binding.val = val;
if (val is IFn)
this.fn = (IFn)val;
else
this.fn = null; //todo, bind to throw stub?
return this;
}
public Object getValue()
{
- Binding binding = getBinding();
if(binding != null)
return binding.val;
throw new InvalidOperationException(this.toString() + " is unbound.");
}
public Object setValue(Object val)
{
+ Binding binding = getBinding();
if(binding != null)
return binding.val;
throw new InvalidOperationException(this.ToString() + " is unbound.");
}
public Object setValue(Object val)
{
Binding b = getThreadBinding();
if(b != null)
return b.val = val;
if(binding == null)
- throw new InvalidOperationException(this.toString() + " is unbound.");
if(val is IFn)
this.fn = (IFn) val;
else
this.fn = null; //todo, bind to throw stub?
return binding.val = val;
}
+ throw new InvalidOperationException(this.ToString() + " is unbound.");
if(val is IFn)
this.fn = (IFn) val;
else
this.fn = null; //todo, bind to throw stub?
return binding.val = val;
}
public Binding pushThreadBinding(Object val) {
Binding ret = new Binding(val, getThreadBinding());
diff --git a/src/jvm/clojure/lang/LispReader.java b/src/jvm/clojure/lang/LispReader.java index a36775c0..ce0d2980 100644 --- a/src/jvm/clojure/lang/LispReader.java +++ b/src/jvm/clojure/lang/LispReader.java @@ -10,10 +10,10 @@ package clojure.lang;
-import java.io.InputStreamReader;
-import java.io.Reader;
+import java.io.*;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
+import java.util.ArrayList;
import java.math.BigInteger;
public class LispReader {
@@ -33,8 +33,11 @@ static Pattern classNamePat = Pattern.compile("([a-zA-Z_][\\w\\.]*)\\."); static{
macros['"'] = new StringReader();
macros[';'] = new CommentReader();
+macros['('] = new ListReader();
+macros[')'] = new UnmatchedDelimiterReader();
+macros['\\'] = new CharacterReader();
}
-static public Object read(LineNumberingPushbackReader r, boolean eofIsError, Object eofValue, boolean isRecursive)
+static public Object read(PushbackReader r, boolean eofIsError, Object eofValue, boolean isRecursive)
throws Exception {
for (; ;)
@@ -54,7 +57,7 @@ static public Object read(LineNumberingPushbackReader r, boolean eofIsError, Obj IFn macroFn = getMacro(ch);
if (macroFn != null)
{
- Object ret = macroFn.invoke(r, ch);
+ Object ret = macroFn.invoke(r, (char)ch);
if(RT.suppressRead())
return null;
//no op macros return the reader
@@ -63,16 +66,16 @@ static public Object read(LineNumberingPushbackReader r, boolean eofIsError, Obj return ret;
}
- String token = readToken(r,ch);
+ String token = readToken(r,(char)ch);
if(RT.suppressRead())
return null;
return interpretToken(token);
}
}
-static private String readToken(LineNumberingPushbackReader r, int initch) throws Exception {
+static private String readToken(PushbackReader r, char initch) throws Exception {
StringBuilder sb = new StringBuilder();
- sb.append((char)initch);
+ sb.append(initch);
for(;;)
{
@@ -166,22 +169,6 @@ static private boolean isMacro(int ch) { }
-public static void main(String[] args){
- LineNumberingPushbackReader r = new LineNumberingPushbackReader(new InputStreamReader(System.in));
- Object ret = null;
- try{
- for(;;){
- ret = LispReader.read(r, true, null, false);
- System.out.println(ret.getClass().getName());
- System.out.println(ret.toString());
- }
- }
- catch(Exception e)
- {
- e.printStackTrace();
- }
-}
-
static class StringReader extends AFn{
public Object invoke(Object reader, Object doublequote) throws Exception {
StringBuilder sb = new StringBuilder();
@@ -219,8 +206,8 @@ static class StringReader extends AFn{ }
return sb.toString();
}
-}
+}
static class CommentReader extends AFn{
public Object invoke(Object reader, Object semicolon) throws Exception {
Reader r = (Reader) reader;
@@ -231,6 +218,99 @@ static class CommentReader extends AFn{ } while (ch != -1 && ch != '\n' && ch != '\r');
return r;
}
+
+}
+static class CharacterReader extends AFn{
+ public Object invoke(Object reader, Object backslash) throws Exception {
+ PushbackReader r = (PushbackReader) reader;
+ int ch = r.read();
+ if(ch == -1)
+ throw new Exception("EOF while reading character");
+ String token = readToken(r,(char)ch);
+ if(token.length() == 1)
+ return token.charAt(0);
+ else if(token.equals("newline"))
+ return '\n';
+ else if(token.equals("space"))
+ return ' ';
+ else if(token.equals("tab"))
+ return '\t';
+ throw new Exception("Unsupported character: \\" + token);
+ }
+
+}
+static class ListReader extends AFn{
+ public Object invoke(Object reader, Object leftparen) throws Exception {
+ PushbackReader r = (PushbackReader) reader;
+ return readDelimitedList(')', r, true);
+ }
+
+}
+static class UnmatchedDelimiterReader extends AFn{
+ public Object invoke(Object reader, Object rightdelim) throws Exception {
+ throw new Exception("Unmatched delimiter: " + rightdelim);
+ }
+
}
+public static ISeq readDelimitedList(char delim, PushbackReader r, boolean isRecursive) throws Exception {
+ ArrayList a = new ArrayList();
+
+ for (; ;)
+ {
+ int ch = r.read();
+
+ while (Character.isWhitespace(ch))
+ ch = r.read();
+
+ if (ch == -1)
+ throw new Exception("EOF while reading");
+
+ if(ch == delim)
+ break;
+
+ IFn macroFn = getMacro(ch);
+ if (macroFn != null)
+ {
+ Object mret = macroFn.invoke(r, (char)ch);
+ //no op macros return the reader
+ if (mret != r)
+ a.add(mret);
+ }
+ else
+ {
+ r.unread(ch);
+
+ Object o = read(r, true, null, isRecursive);
+ if (o != r)
+ a.add(o);
+ }
+ }
+
+ ISeq ret = null;
+ for(int i=a.size()-1;i>=0;--i)
+ ret = RT.cons(a.get(i), ret);
+ return ret;
+}
+
+public static void main(String[] args){
+ LineNumberingPushbackReader r = new LineNumberingPushbackReader(new InputStreamReader(System.in));
+ OutputStreamWriter w = new OutputStreamWriter(System.out);
+ Object ret = null;
+ try{
+ for(;;)
+ {
+ ret = LispReader.read(r, true, null, false);
+ RT.print(ret, w);
+ w.write('\n');
+ w.flush();
+ }
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ }
+}
+
+
}
diff --git a/src/jvm/clojure/lang/RT.java b/src/jvm/clojure/lang/RT.java index aea8e677..645da926 100644 --- a/src/jvm/clojure/lang/RT.java +++ b/src/jvm/clojure/lang/RT.java @@ -15,6 +15,8 @@ package clojure.lang; import java.util.Iterator; import java.io.Reader; import java.io.PushbackReader; +import java.io.Writer; +import java.io.IOException; public class RT{ @@ -316,6 +318,47 @@ static public boolean suppressRead(){ return false; } +static public void print(Object x, Writer w) throws Exception { + //todo - make extensible + if(x == null) + w.write("null"); + else if(x instanceof ISeq) + { + w.write('('); + for(ISeq s = (ISeq)x;s != null;s = s.rest()) + { + print(s.first(), w); + if(s.rest()!=null) + w.write(' '); + } + w.write(')'); + } + else if(x instanceof String) + { + w.write('"'); + w.write(x.toString()); + w.write('"'); + } + else if(x instanceof Character) + { + w.write('\\'); + char c = ((Character)x).charValue(); + switch(c){ + case '\n': + w.write("newline"); + break; + case '\t': + w.write("tab"); + break; + case ' ': + w.write("space"); + break; + default: + w.write(c); + } + } + else w.write(x.toString()); +} ///////////////////////////////// values ////////////////////////// static public Object setValues(Object... vals) |