From 59cd48342c2a1c523b7c7ea09641012c553369be Mon Sep 17 00:00:00 2001 From: Brandon Bloom Date: Mon, 26 Mar 2012 22:51:20 -0700 Subject: [PATCH] Enhance compiler with :column metadata --- src/jvm/clojure/lang/Compiler.java | 183 +++++++++++++------- .../clojure/lang/LineNumberingPushbackReader.java | 18 ++- src/jvm/clojure/lang/LispReader.java | 26 ++- src/jvm/clojure/lang/RT.java | 7 + test/clojure/test_clojure/compilation.clj | 3 + test/clojure/test_clojure/rt.clj | 8 +- 6 files changed, 175 insertions(+), 70 deletions(-) diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index 0898f07..6664972 100644 --- a/src/jvm/clojure/lang/Compiler.java +++ b/src/jvm/clojure/lang/Compiler.java @@ -265,10 +265,13 @@ static Object elideMeta(Object m){ //Integer static final public Var LINE = Var.create(0).setDynamic(); +static final public Var COLUMN = Var.create(0).setDynamic(); //Integer static final public Var LINE_BEFORE = Var.create(0).setDynamic(); +static final public Var COLUMN_BEFORE = Var.create(0).setDynamic(); static final public Var LINE_AFTER = Var.create(0).setDynamic(); +static final public Var COLUMN_AFTER = Var.create(0).setDynamic(); //Integer static final public Var NEXT_LOCAL_NUM = Var.create(0).setDynamic(); @@ -359,15 +362,17 @@ static class DefExpr implements Expr{ public final boolean isDynamic; public final String source; public final int line; + public final int column; final static Method bindRootMethod = Method.getMethod("void bindRoot(Object)"); final static Method setTagMethod = Method.getMethod("void setTag(clojure.lang.Symbol)"); final static Method setMetaMethod = Method.getMethod("void setMeta(clojure.lang.IPersistentMap)"); final static Method setDynamicMethod = Method.getMethod("clojure.lang.Var setDynamic(boolean)"); final static Method symintern = Method.getMethod("clojure.lang.Symbol intern(String, String)"); - public DefExpr(String source, int line, Var var, Expr init, Expr meta, boolean initProvided, boolean isDynamic){ + public DefExpr(String source, int line, int column, Var var, Expr init, Expr meta, boolean initProvided, boolean isDynamic){ this.source = source; this.line = line; + this.column = column; this.var = var; this.init = init; this.meta = meta; @@ -381,7 +386,8 @@ static class DefExpr implements Expr{ Keyword k = ((KeywordExpr) expr.keyvals.nth(i)).k; if ((k != RT.FILE_KEY) && (k != RT.DECLARED_KEY) && - (k != RT.LINE_KEY)) + (k != RT.LINE_KEY) && + (k != RT.COLUMN_KEY)) return true; } return false; @@ -408,7 +414,7 @@ static class DefExpr implements Expr{ catch(Throwable e) { if(!(e instanceof CompilerException)) - throw new CompilerException(source, line, e); + throw new CompilerException(source, line, column, e); else throw (CompilerException) e; } @@ -502,20 +508,21 @@ static class DefExpr implements Expr{ } Object source_path = SOURCE_PATH.get(); source_path = source_path == null ? "NO_SOURCE_FILE" : source_path; - mm = (IPersistentMap) RT.assoc(mm, RT.LINE_KEY, LINE.get()).assoc(RT.FILE_KEY, source_path); + mm = (IPersistentMap) RT.assoc(mm, RT.LINE_KEY, LINE.get()).assoc(RT.COLUMN_KEY, COLUMN.get()).assoc(RT.FILE_KEY, source_path); if (docstring != null) mm = (IPersistentMap) RT.assoc(mm, RT.DOC_KEY, docstring); // mm = mm.without(RT.DOC_KEY) // .without(Keyword.intern(null, "arglists")) // .without(RT.FILE_KEY) // .without(RT.LINE_KEY) +// .without(RT.COLUMN_KEY) // .without(Keyword.intern(null, "ns")) // .without(Keyword.intern(null, "name")) // .without(Keyword.intern(null, "added")) // .without(Keyword.intern(null, "static")); mm = (IPersistentMap) elideMeta(mm); Expr meta = mm.count()==0 ? null:analyze(context == C.EVAL ? context : C.EXPRESSION, mm); - return new DefExpr((String) SOURCE.deref(), (Integer) LINE.deref(), + return new DefExpr((String) SOURCE.deref(), (Integer) LINE.deref(), (Integer) COLUMN.deref(), v, analyze(context == C.EVAL ? context : C.EXPRESSION, RT.third(form), v.sym.name), meta, RT.count(form) == 3, isDynamic); } @@ -895,6 +902,7 @@ static public abstract class HostExpr implements Expr, MaybePrimitiveExpr{ //determine static or instance //static target must be symbol, either fully.qualified.Classname or Classname that has been imported int line = (Integer) LINE.deref(); + int column = (Integer) COLUMN.deref(); String source = (String) SOURCE.deref(); Class c = maybeClass(RT.second(form), false); //at this point c will be non-null if static @@ -920,9 +928,9 @@ static public abstract class HostExpr implements Expr, MaybePrimitiveExpr{ :(Symbol) RT.third(form); Symbol tag = tagOf(form); if(c != null) { - return new StaticFieldExpr(line, c, munge(sym.name), tag); + return new StaticFieldExpr(line, column, c, munge(sym.name), tag); } else - return new InstanceFieldExpr(line, instance, munge(sym.name), tag); + return new InstanceFieldExpr(line, column, instance, munge(sym.name), tag); } else { @@ -935,9 +943,9 @@ static public abstract class HostExpr implements Expr, MaybePrimitiveExpr{ for(ISeq s = RT.next(call); s != null; s = s.next()) args = args.cons(analyze(context == C.EVAL ? context : C.EXPRESSION, s.first())); if(c != null) - return new StaticMethodExpr(source, line, tag, c, munge(sym.name), args); + return new StaticMethodExpr(source, line, column, tag, c, munge(sym.name), args); else - return new InstanceMethodExpr(source, line, tag, instance, munge(sym.name), args); + return new InstanceMethodExpr(source, line, column, tag, instance, munge(sym.name), args); } } } @@ -1058,23 +1066,25 @@ static class InstanceFieldExpr extends FieldExpr implements AssignableExpr{ public final java.lang.reflect.Field field; public final String fieldName; public final int line; + public final int column; public final Symbol tag; final static Method invokeNoArgInstanceMember = Method.getMethod("Object invokeNoArgInstanceMember(Object,String)"); final static Method setInstanceFieldMethod = Method.getMethod("Object setInstanceField(Object,String,Object)"); - public InstanceFieldExpr(int line, Expr target, String fieldName, Symbol tag) { + public InstanceFieldExpr(int line, int column, Expr target, String fieldName, Symbol tag) { this.target = target; this.targetClass = target.hasJavaClass() ? target.getJavaClass() : null; this.field = targetClass != null ? Reflector.getField(targetClass, fieldName, false) : null; this.fieldName = fieldName; this.line = line; + this.column = column; this.tag = tag; if(field == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref())) { RT.errPrintWriter() - .format("Reflection warning, %s:%d - reference to field %s can't be resolved.\n", - SOURCE_PATH.deref(), line, fieldName); + .format("Reflection warning, %s:%d:%d - reference to field %s can't be resolved.\n", + SOURCE_PATH.deref(), line, column, fieldName); } } @@ -1168,11 +1178,13 @@ static class StaticFieldExpr extends FieldExpr implements AssignableExpr{ // final static Method getStaticFieldMethod = Method.getMethod("Object getStaticField(String,String)"); // final static Method setStaticFieldMethod = Method.getMethod("Object setStaticField(String,String,Object)"); final int line; + final int column; - public StaticFieldExpr(int line, Class c, String fieldName, Symbol tag) { + public StaticFieldExpr(int line, int column, Class c, String fieldName, Symbol tag) { //this.className = className; this.fieldName = fieldName; this.line = line; + this.column = column; //c = Class.forName(className); this.c = c; try @@ -1357,6 +1369,7 @@ static class InstanceMethodExpr extends MethodExpr{ public final IPersistentVector args; public final String source; public final int line; + public final int column; public final Symbol tag; public final java.lang.reflect.Method method; @@ -1364,10 +1377,11 @@ static class InstanceMethodExpr extends MethodExpr{ Method.getMethod("Object invokeInstanceMethod(Object,String,Object[])"); - public InstanceMethodExpr(String source, int line, Symbol tag, Expr target, String methodName, IPersistentVector args) + public InstanceMethodExpr(String source, int line, int column, Symbol tag, Expr target, String methodName, IPersistentVector args) { this.source = source; this.line = line; + this.column = column; this.args = args; this.methodName = methodName; this.target = target; @@ -1409,8 +1423,8 @@ static class InstanceMethodExpr extends MethodExpr{ if(method == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref())) { RT.errPrintWriter() - .format("Reflection warning, %s:%d - call to %s can't be resolved.\n", - SOURCE_PATH.deref(), line, methodName); + .format("Reflection warning, %s:%d:%d - call to %s can't be resolved.\n", + SOURCE_PATH.deref(), line, column, methodName); } } @@ -1432,7 +1446,7 @@ static class InstanceMethodExpr extends MethodExpr{ catch(Throwable e) { if(!(e instanceof CompilerException)) - throw new CompilerException(source, line, e); + throw new CompilerException(source, line, column, e); else throw (CompilerException) e; } @@ -1521,6 +1535,7 @@ static class StaticMethodExpr extends MethodExpr{ public final IPersistentVector args; public final String source; public final int line; + public final int column; public final java.lang.reflect.Method method; public final Symbol tag; final static Method forNameMethod = Method.getMethod("Class forName(String)"); @@ -1528,13 +1543,14 @@ static class StaticMethodExpr extends MethodExpr{ Method.getMethod("Object invokeStaticMethod(Class,String,Object[])"); - public StaticMethodExpr(String source, int line, Symbol tag, Class c, String methodName, IPersistentVector args) + public StaticMethodExpr(String source, int line, int column, Symbol tag, Class c, String methodName, IPersistentVector args) { this.c = c; this.methodName = methodName; this.args = args; this.source = source; this.line = line; + this.column = column; this.tag = tag; List methods = Reflector.getMethods(c, args.count(), methodName, true); @@ -1558,8 +1574,8 @@ static class StaticMethodExpr extends MethodExpr{ if(method == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref())) { RT.errPrintWriter() - .format("Reflection warning, %s:%d - call to %s can't be resolved.\n", - SOURCE_PATH.deref(), line, methodName); + .format("Reflection warning, %s:%d:%d - call to %s can't be resolved.\n", + SOURCE_PATH.deref(), line, column, methodName); } } @@ -1580,7 +1596,7 @@ static class StaticMethodExpr extends MethodExpr{ catch(Throwable e) { if(!(e instanceof CompilerException)) - throw new CompilerException(source, line, e); + throw new CompilerException(source, line, column, e); else throw (CompilerException) e; } @@ -2372,7 +2388,7 @@ public static class NewExpr implements Expr{ final static Method forNameMethod = Method.getMethod("Class forName(String)"); - public NewExpr(Class c, IPersistentVector args, int line) { + public NewExpr(Class c, IPersistentVector args, int line, int column) { this.args = args; this.c = c; Constructor[] allctors = c.getConstructors(); @@ -2402,8 +2418,8 @@ public static class NewExpr implements Expr{ if(ctor == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref())) { RT.errPrintWriter() - .format("Reflection warning, %s:%d - call to %s ctor can't be resolved.\n", - SOURCE_PATH.deref(), line, c.getName()); + .format("Reflection warning, %s:%d:%d - call to %s ctor can't be resolved.\n", + SOURCE_PATH.deref(), line, column, c.getName()); } } @@ -2466,6 +2482,7 @@ public static class NewExpr implements Expr{ static class Parser implements IParser{ public Expr parse(C context, Object frm) { int line = (Integer) LINE.deref(); + int column = (Integer) COLUMN.deref(); ISeq form = (ISeq) frm; //(new Classname args...) if(form.count() < 2) @@ -2476,7 +2493,7 @@ public static class NewExpr implements Expr{ PersistentVector args = PersistentVector.EMPTY; for(ISeq s = RT.next(RT.next(form)); s != null; s = s.next()) args = args.cons(analyze(context == C.EVAL ? context : C.EXPRESSION, s.first())); - return new NewExpr(c, args, line); + return new NewExpr(c, args, line, column); } } @@ -2524,13 +2541,15 @@ public static class IfExpr implements Expr, MaybePrimitiveExpr{ public final Expr thenExpr; public final Expr elseExpr; public final int line; + public final int column; - public IfExpr(int line, Expr testExpr, Expr thenExpr, Expr elseExpr){ + public IfExpr(int line, int column, Expr testExpr, Expr thenExpr, Expr elseExpr){ this.testExpr = testExpr; this.thenExpr = thenExpr; this.elseExpr = elseExpr; this.line = line; + this.column = column; } public Object eval() { @@ -2653,6 +2672,7 @@ public static class IfExpr implements Expr, MaybePrimitiveExpr{ Var.popThreadBindings(); } return new IfExpr((Integer) LINE.deref(), + (Integer) COLUMN.deref(), testexpr, thenexpr, elseexpr); @@ -2982,15 +3002,17 @@ static class KeywordInvokeExpr implements Expr{ public final Object tag; public final Expr target; public final int line; + public final int column; public final int siteIndex; public final String source; static Type ILOOKUP_TYPE = Type.getType(ILookup.class); - public KeywordInvokeExpr(String source, int line, Symbol tag, KeywordExpr kw, Expr target){ + public KeywordInvokeExpr(String source, int line, int column, Symbol tag, KeywordExpr kw, Expr target){ this.source = source; this.kw = kw; this.target = target; this.line = line; + this.column = column; this.tag = tag; this.siteIndex = registerKeywordCallsite(kw.k); } @@ -3003,7 +3025,7 @@ static class KeywordInvokeExpr implements Expr{ catch(Throwable e) { if(!(e instanceof CompilerException)) - throw new CompilerException(source, line, e); + throw new CompilerException(source, line, column, e); else throw (CompilerException) e; } @@ -3056,13 +3078,15 @@ static class KeywordInvokeExpr implements Expr{ // public final Object tag; // public final Expr target; // public final int line; +// public final int column; // public final String source; // -// public KeywordSiteInvokeExpr(String source, int line, Symbol tag, Expr site, Expr target){ +// public KeywordSiteInvokeExpr(String source, int line, int column, Symbol tag, Expr site, Expr target){ // this.source = source; // this.site = site; // this.target = target; // this.line = line; +// this.column = column; // this.tag = tag; // } // @@ -3075,7 +3099,7 @@ static class KeywordInvokeExpr implements Expr{ // catch(Throwable e) // { // if(!(e instanceof CompilerException)) -// throw new CompilerException(source, line, e); +// throw new CompilerException(source, line, column, e); // else // throw (CompilerException) e; // } @@ -3307,6 +3331,7 @@ static class InvokeExpr implements Expr{ public final Object tag; public final IPersistentVector args; public final int line; + public final int column; public final String source; public boolean isProtocol = false; public boolean isDirect = false; @@ -3316,11 +3341,12 @@ static class InvokeExpr implements Expr{ static Keyword onKey = Keyword.intern("on"); static Keyword methodMapKey = Keyword.intern("method-map"); - public InvokeExpr(String source, int line, Symbol tag, Expr fexpr, IPersistentVector args) { + public InvokeExpr(String source, int line, int column, Symbol tag, Expr fexpr, IPersistentVector args) { this.source = source; this.fexpr = fexpr; this.args = args; this.line = line; + this.column = column; if(fexpr instanceof VarExpr) { Var fvar = ((VarExpr)fexpr).var; @@ -3384,7 +3410,7 @@ static class InvokeExpr implements Expr{ catch(Throwable e) { if(!(e instanceof CompilerException)) - throw new CompilerException(source, line, e); + throw new CompilerException(source, line, column, e); else throw (CompilerException) e; } @@ -3539,7 +3565,7 @@ static class InvokeExpr implements Expr{ { // fexpr = new ConstantExpr(new KeywordCallSite(((KeywordExpr)fexpr).k)); Expr target = analyze(context, RT.second(form)); - return new KeywordInvokeExpr((String) SOURCE.deref(), (Integer) LINE.deref(), tagOf(form), + return new KeywordInvokeExpr((String) SOURCE.deref(), (Integer) LINE.deref(), (Integer) COLUMN.deref(), tagOf(form), (KeywordExpr) fexpr, target); } PersistentVector args = PersistentVector.EMPTY; @@ -3551,7 +3577,7 @@ static class InvokeExpr implements Expr{ // throw new IllegalArgumentException( // String.format("No more than %d args supported", MAX_POSITIONAL_ARITY)); - return new InvokeExpr((String) SOURCE.deref(), (Integer) LINE.deref(), tagOf(form), fexpr, args); + return new InvokeExpr((String) SOURCE.deref(), (Integer) LINE.deref(), (Integer) COLUMN.deref(), tagOf(form), fexpr, args); } } @@ -3667,6 +3693,7 @@ static public class FnExpr extends ObjExpr{ if(RT.second(form) instanceof IPersistentVector) form = RT.list(FN, RT.next(form)); fn.line = (Integer) LINE.deref(); + fn.column = (Integer) COLUMN.deref(); FnMethod[] methodArray = new FnMethod[MAX_POSITIONAL_ARITY + 1]; FnMethod variadicMethod = null; for(ISeq s = RT.next(form); s != null; s = RT.next(s)) @@ -3723,7 +3750,7 @@ static public class FnExpr extends ObjExpr{ fn.hasPrimSigs = prims.size() > 0; IPersistentMap fmeta = RT.meta(origForm); if(fmeta != null) - fmeta = fmeta.without(RT.LINE_KEY).without(RT.FILE_KEY); + fmeta = fmeta.without(RT.LINE_KEY).without(RT.COLUMN_KEY).without(RT.FILE_KEY); fn.hasMeta = RT.count(fmeta) > 0; @@ -3806,6 +3833,7 @@ static public class ObjExpr implements Expr{ IPersistentMap vars = PersistentHashMap.EMPTY; Class compiledClass; int line; + int column; PersistentVector constants; int constantsID; int altCtorDrops = 0; @@ -3861,6 +3889,10 @@ static public class ObjExpr implements Expr{ return line; } + public final int column(){ + return column; + } + public final PersistentVector constants(){ return constants; } @@ -3930,6 +3962,8 @@ static public class ObjExpr implements Expr{ String source = (String) SOURCE.deref(); int lineBefore = (Integer) LINE_BEFORE.deref(); int lineAfter = (Integer) LINE_AFTER.deref() + 1; + int columnBefore = (Integer) COLUMN_BEFORE.deref(); + int columnAfter = (Integer) COLUMN_AFTER.deref() + 1; if(source != null && SOURCE_PATH.deref() != null) { @@ -4943,6 +4977,7 @@ public static class FnMethod extends ObjMethod{ { FnMethod method = new FnMethod(objx, (ObjMethod) METHOD.deref()); method.line = (Integer) LINE.deref(); + method.column = (Integer) COLUMN.deref(); //register as the current method and set up a new env frame PathNode pnode = (PathNode) CLEAR_PATH.get(); if(pnode == null) @@ -5312,6 +5347,7 @@ abstract public static class ObjMethod{ PersistentVector argLocals; int maxLocal = 0; int line; + int column; PersistentHashSet localsUsedInCatchFinally = PersistentHashSet.EMPTY; protected IPersistentMap methodMeta; @@ -5340,6 +5376,10 @@ abstract public static class ObjMethod{ return line; } + public final int column(){ + return column; + } + public ObjMethod(ObjExpr objx, ObjMethod parent){ this.parent = parent; this.objx = objx; @@ -5885,14 +5925,14 @@ public static class LetExpr implements Expr, MaybePrimitiveExpr{ { if(recurMismatches != null && RT.booleanCast(recurMismatches.nth(i/2))) { - init = new StaticMethodExpr("", 0, null, RT.class, "box", RT.vector(init)); + init = new StaticMethodExpr("", 0, 0, null, RT.class, "box", RT.vector(init)); if(RT.booleanCast(RT.WARN_ON_REFLECTION.deref())) RT.errPrintWriter().println("Auto-boxing loop arg: " + sym); } else if(maybePrimitiveType(init) == int.class) - init = new StaticMethodExpr("", 0, null, RT.class, "longCast", RT.vector(init)); + init = new StaticMethodExpr("", 0, 0, null, RT.class, "longCast", RT.vector(init)); else if(maybePrimitiveType(init) == float.class) - init = new StaticMethodExpr("", 0, null, RT.class, "doubleCast", RT.vector(init)); + init = new StaticMethodExpr("", 0, 0, null, RT.class, "doubleCast", RT.vector(init)); } //sequential enhancement of env (like Lisp let*) LocalBinding lb = registerLocal(sym, tagOf(sym), init,false); @@ -6033,13 +6073,15 @@ public static class RecurExpr implements Expr{ public final IPersistentVector args; public final IPersistentVector loopLocals; final int line; + final int column; final String source; - public RecurExpr(IPersistentVector loopLocals, IPersistentVector args, int line, String source){ + public RecurExpr(IPersistentVector loopLocals, IPersistentVector args, int line, int column, String source){ this.loopLocals = loopLocals; this.args = args; this.line = line; + this.column = column; this.source = source; } @@ -6138,6 +6180,7 @@ public static class RecurExpr implements Expr{ static class Parser implements IParser{ public Expr parse(C context, Object frm) { int line = (Integer) LINE.deref(); + int column = (Integer) COLUMN.deref(); String source = (String) SOURCE.deref(); ISeq form = (ISeq) frm; @@ -6194,7 +6237,7 @@ public static class RecurExpr implements Expr{ } } } - return new RecurExpr(loopLocals, args, line, source); + return new RecurExpr(loopLocals, args, line, column, source); } } } @@ -6278,7 +6321,7 @@ private static Expr analyze(C context, Object form, String name) { catch(Throwable e) { if(!(e instanceof CompilerException)) - throw new CompilerException((String) SOURCE_PATH.deref(), (Integer) LINE.deref(), e); + throw new CompilerException((String) SOURCE_PATH.deref(), (Integer) LINE.deref(), (Integer) COLUMN.deref(), e); else throw (CompilerException) e; } @@ -6287,8 +6330,8 @@ private static Expr analyze(C context, Object form, String name) { static public class CompilerException extends RuntimeException{ final public String source; - public CompilerException(String source, int line, Throwable cause){ - super(errorMsg(source, line, cause.toString()), cause); + public CompilerException(String source, int line, int column, Throwable cause){ + super(errorMsg(source, line, column, cause.toString()), cause); this.source = source; } @@ -6432,10 +6475,13 @@ static Object macroexpand(Object form) { private static Expr analyzeSeq(C context, ISeq form, String name) { Integer line = (Integer) LINE.deref(); + Integer column = (Integer) COLUMN.deref(); if(RT.meta(form) != null && RT.meta(form).containsKey(RT.LINE_KEY)) line = (Integer) RT.meta(form).valAt(RT.LINE_KEY); + if(RT.meta(form) != null && RT.meta(form).containsKey(RT.COLUMN_KEY)) + column = (Integer) RT.meta(form).valAt(RT.COLUMN_KEY); Var.pushThreadBindings( - RT.map(LINE, line)); + RT.map(LINE, line, COLUMN, column)); try { Object me = macroexpand1(form); @@ -6459,7 +6505,7 @@ private static Expr analyzeSeq(C context, ISeq form, String name) { catch(Throwable e) { if(!(e instanceof CompilerException)) - throw new CompilerException((String) SOURCE_PATH.deref(), (Integer) LINE.deref(), e); + throw new CompilerException((String) SOURCE_PATH.deref(), (Integer) LINE.deref(), (Integer) COLUMN.deref(), e); else throw (CompilerException) e; } @@ -6469,8 +6515,8 @@ private static Expr analyzeSeq(C context, ISeq form, String name) { } } -static String errorMsg(String source, int line, String s){ - return String.format("%s, compiling:(%s:%d)", s, source, line); +static String errorMsg(String source, int line, int column, String s){ + return String.format("%s, compiling:(%s:%d:%d)", s, source, line, column); } public static Object eval(Object form) { @@ -6487,9 +6533,12 @@ public static Object eval(Object form, boolean freshLoader) { try { Integer line = (Integer) LINE.deref(); + Integer column = (Integer) COLUMN.deref(); if(RT.meta(form) != null && RT.meta(form).containsKey(RT.LINE_KEY)) line = (Integer) RT.meta(form).valAt(RT.LINE_KEY); - Var.pushThreadBindings(RT.map(LINE, line)); + if(RT.meta(form) != null && RT.meta(form).containsKey(RT.COLUMN_KEY)) + column = (Integer) RT.meta(form).valAt(RT.COLUMN_KEY); + Var.pushThreadBindings(RT.map(LINE, line, COLUMN, column)); try { form = macroexpand(form); @@ -6659,7 +6708,7 @@ private static Expr analyzeSymbol(Symbol sym) { if(c != null) { if(Reflector.getField(c, sym.name, true) != null) - return new StaticFieldExpr((Integer) LINE.deref(), c, sym.name, tag); + return new StaticFieldExpr((Integer) LINE.deref(), (Integer) COLUMN.deref(), c, sym.name, tag); throw Util.runtimeException("Unable to find static field: " + sym.name + " in " + c); } } @@ -6937,7 +6986,9 @@ public static Object load(Reader rdr, String sourcePath, String sourceName) { NEXT_LOCAL_NUM, 0, RT.CURRENT_NS, RT.CURRENT_NS.deref(), LINE_BEFORE, pushbackReader.getLineNumber(), - LINE_AFTER, pushbackReader.getLineNumber() + COLUMN_BEFORE, pushbackReader.getColumnNumber(), + LINE_AFTER, pushbackReader.getLineNumber(), + COLUMN_AFTER, pushbackReader.getColumnNumber() ,RT.UNCHECKED_MATH, RT.UNCHECKED_MATH.deref() ,RT.WARN_ON_REFLECTION, RT.WARN_ON_REFLECTION.deref() ,RT.DATA_READERS, RT.DATA_READERS.deref() @@ -6949,13 +7000,15 @@ public static Object load(Reader rdr, String sourcePath, String sourceName) { r = LispReader.read(pushbackReader, false, EOF, false)) { LINE_AFTER.set(pushbackReader.getLineNumber()); + COLUMN_AFTER.set(pushbackReader.getColumnNumber()); ret = eval(r,false); LINE_BEFORE.set(pushbackReader.getLineNumber()); + COLUMN_BEFORE.set(pushbackReader.getColumnNumber()); } } catch(LispReader.ReaderException e) { - throw new CompilerException(sourcePath, e.line, e.getCause()); + throw new CompilerException(sourcePath, e.line, e.column, e.getCause()); } finally { @@ -7009,10 +7062,13 @@ public static ILookupThunk getLookupThunk(Object target, Keyword k){ static void compile1(GeneratorAdapter gen, ObjExpr objx, Object form) { Integer line = (Integer) LINE.deref(); + Integer column = (Integer) COLUMN.deref(); if(RT.meta(form) != null && RT.meta(form).containsKey(RT.LINE_KEY)) line = (Integer) RT.meta(form).valAt(RT.LINE_KEY); + if(RT.meta(form) != null && RT.meta(form).containsKey(RT.COLUMN_KEY)) + column = (Integer) RT.meta(form).valAt(RT.COLUMN_KEY); Var.pushThreadBindings( - RT.map(LINE, line + RT.map(LINE, line, COLUMN, column ,LOADER, RT.makeClassLoader() )); try @@ -7059,7 +7115,9 @@ public static Object compile(Reader rdr, String sourcePath, String sourceName) t NEXT_LOCAL_NUM, 0, RT.CURRENT_NS, RT.CURRENT_NS.deref(), LINE_BEFORE, pushbackReader.getLineNumber(), + COLUMN_BEFORE, pushbackReader.getColumnNumber(), LINE_AFTER, pushbackReader.getLineNumber(), + COLUMN_AFTER, pushbackReader.getColumnNumber(), CONSTANTS, PersistentVector.EMPTY, CONSTANT_IDS, new IdentityHashMap(), KEYWORDS, PersistentHashMap.EMPTY, @@ -7094,8 +7152,10 @@ public static Object compile(Reader rdr, String sourcePath, String sourceName) t r = LispReader.read(pushbackReader, false, EOF, false)) { LINE_AFTER.set(pushbackReader.getLineNumber()); + COLUMN_AFTER.set(pushbackReader.getColumnNumber()); compile1(gen, objx, r); LINE_BEFORE.set(pushbackReader.getLineNumber()); + COLUMN_BEFORE.set(pushbackReader.getColumnNumber()); } //end of load gen.returnValue(); @@ -7187,7 +7247,7 @@ public static Object compile(Reader rdr, String sourcePath, String sourceName) t } catch(LispReader.ReaderException e) { - throw new CompilerException(sourcePath, e.line, e.getCause()); + throw new CompilerException(sourcePath, e.line, e.column, e.getCause()); } finally { @@ -7341,6 +7401,7 @@ static public class NewInstanceExpr extends ObjExpr{ //now (methodname [args] body)* ret.line = (Integer) LINE.deref(); + ret.column = (Integer) COLUMN.deref(); IPersistentCollection methods = null; for(ISeq s = methodForms; s != null; s = RT.next(s)) { @@ -7701,6 +7762,7 @@ public static class NewInstanceMethod extends ObjMethod{ try { method.line = (Integer) LINE.deref(); + method.column = (Integer) COLUMN.deref(); //register as the current method and set up a new env frame PathNode pnode = new PathNode(PATHTYPE.PATH, (PathNode) CLEAR_PATH.get()); Var.pushThreadBindings( @@ -8003,6 +8065,7 @@ public static class CaseExpr implements Expr, MaybePrimitiveExpr{ public final Set skipCheck; public final Class returnType; public final int line; + public final int column; final static Type NUMBER_TYPE = Type.getType(Number.class); final static Method intValueMethod = Method.getMethod("int intValue()"); @@ -8016,7 +8079,7 @@ public static class CaseExpr implements Expr, MaybePrimitiveExpr{ final static Keyword hashEquivKey = Keyword.intern(null, "hash-equiv"); final static Keyword intKey = Keyword.intern(null, "int"); //(case* expr shift mask default map table-type test-type skip-check?) - public CaseExpr(int line, LocalBindingExpr expr, int shift, int mask, int low, int high, Expr defaultExpr, + public CaseExpr(int line, int column, LocalBindingExpr expr, int shift, int mask, int low, int high, Expr defaultExpr, SortedMap tests,HashMap thens, Keyword switchType, Keyword testType, Set skipCheck){ this.expr = expr; this.shift = shift; @@ -8027,6 +8090,7 @@ public static class CaseExpr implements Expr, MaybePrimitiveExpr{ this.tests = tests; this.thens = thens; this.line = line; + this.column = column; if (switchType != compactKey && switchType != sparseKey) throw new IllegalArgumentException("Unexpected switch type: "+switchType); this.switchType = switchType; @@ -8040,8 +8104,8 @@ public static class CaseExpr implements Expr, MaybePrimitiveExpr{ if(RT.count(skipCheck) > 0 && RT.booleanCast(RT.WARN_ON_REFLECTION.deref())) { RT.errPrintWriter() - .format("Performance warning, %s:%d - hash collision of some case test constants; if selected, those entries will be tested sequentially.\n", - SOURCE_PATH.deref(), line); + .format("Performance warning, %s:%d:%d - hash collision of some case test constants; if selected, those entries will be tested sequentially.\n", + SOURCE_PATH.deref(), line, column); } } @@ -8145,8 +8209,8 @@ public static class CaseExpr implements Expr, MaybePrimitiveExpr{ if(RT.booleanCast(RT.WARN_ON_REFLECTION.deref())) { RT.errPrintWriter() - .format("Performance warning, %s:%d - case has int tests, but tested expression is not primitive.\n", - SOURCE_PATH.deref(), line); + .format("Performance warning, %s:%d:%d - case has int tests, but tested expression is not primitive.\n", + SOURCE_PATH.deref(), line, column); } expr.emit(C.EXPRESSION, objx, gen); gen.instanceOf(NUMBER_TYPE); @@ -8301,7 +8365,8 @@ public static class CaseExpr implements Expr, MaybePrimitiveExpr{ } int line = ((Number)LINE.deref()).intValue(); - return new CaseExpr(line, testexpr, shift, mask, low, high, + int column = ((Number)COLUMN.deref()).intValue(); + return new CaseExpr(line, column, testexpr, shift, mask, low, high, defaultExpr, tests, thens, switchType, testType, skipCheck); } } diff --git a/src/jvm/clojure/lang/LineNumberingPushbackReader.java b/src/jvm/clojure/lang/LineNumberingPushbackReader.java index ad81ce9..c52c8f6 100644 --- a/src/jvm/clojure/lang/LineNumberingPushbackReader.java +++ b/src/jvm/clojure/lang/LineNumberingPushbackReader.java @@ -27,6 +27,7 @@ private static final int newline = (int) '\n'; private boolean _atLineStart = true; private boolean _prev; +private int _columnNumber = 1; public LineNumberingPushbackReader(Reader r){ super(new LineNumberReader(r)); @@ -36,16 +37,30 @@ public int getLineNumber(){ return ((LineNumberReader) in).getLineNumber() + 1; } +public int getColumnNumber(){ + return _columnNumber; +} + public int read() throws IOException{ int c = super.read(); _prev = _atLineStart; - _atLineStart = (c == newline) || (c == -1); + if((c == newline) || (c == -1)) + { + _atLineStart = true; + _columnNumber = 1; + } + else + { + _atLineStart = false; + _columnNumber++; + } return c; } public void unread(int c) throws IOException{ super.unread(c); _atLineStart = _prev; + _columnNumber--; } public String readLine() throws IOException{ @@ -64,6 +79,7 @@ public String readLine() throws IOException{ line = (rest == null) ? first : first + rest; _prev = false; _atLineStart = true; + _columnNumber = 1; break; } return line; diff --git a/src/jvm/clojure/lang/LispReader.java b/src/jvm/clojure/lang/LispReader.java index 8f67b3b..1a3fa12 100644 --- a/src/jvm/clojure/lang/LispReader.java +++ b/src/jvm/clojure/lang/LispReader.java @@ -129,10 +129,12 @@ static void unread(PushbackReader r, int ch) { public static class ReaderException extends RuntimeException{ final int line; + final int column; - public ReaderException(int line, Throwable cause){ + public ReaderException(int line, int column, Throwable cause){ super(cause); this.line = line; + this.column = column; } } @@ -212,7 +214,7 @@ static public Object read(PushbackReader r, boolean eofIsError, Object eofValue, throw Util.sneakyThrow(e); LineNumberingPushbackReader rdr = (LineNumberingPushbackReader) r; //throw Util.runtimeException(String.format("ReaderError:(%d,1) %s", rdr.getLineNumber(), e.getMessage()), e); - throw new ReaderException(rdr.getLineNumber(), e); + throw new ReaderException(rdr.getLineNumber(), rdr.getColumnNumber(), e); } } @@ -709,8 +711,12 @@ public static class MetaReader extends AFn{ public Object invoke(Object reader, Object caret) { PushbackReader r = (PushbackReader) reader; int line = -1; + int column = -1; if(r instanceof LineNumberingPushbackReader) + { line = ((LineNumberingPushbackReader) r).getLineNumber(); + column = ((LineNumberingPushbackReader) r).getColumnNumber(); + } Object meta = read(r, true, null, true); if(meta instanceof Symbol || meta instanceof String) meta = RT.map(RT.TAG_KEY, meta); @@ -723,7 +729,9 @@ public static class MetaReader extends AFn{ if(o instanceof IMeta) { if(line != -1 && o instanceof ISeq) - meta = ((IPersistentMap) meta).assoc(RT.LINE_KEY, line); + { + meta = ((IPersistentMap) meta).assoc(RT.LINE_KEY, line).assoc(RT.COLUMN_KEY, column); + } if(o instanceof IReference) { ((IReference)o).resetMeta((IPersistentMap) meta); @@ -845,8 +853,8 @@ public static class SyntaxQuoteReader extends AFn{ if(form instanceof IObj && RT.meta(form) != null) { - //filter line numbers - IPersistentMap newMeta = ((IObj) form).meta().without(RT.LINE_KEY); + //filter line and column numbers + IPersistentMap newMeta = ((IObj) form).meta().without(RT.LINE_KEY).without(RT.COLUMN_KEY); if(newMeta.count() > 0) return RT.list(WITH_META, ret, syntaxQuote(((IObj) form).meta())); } @@ -957,15 +965,21 @@ public static class ListReader extends AFn{ public Object invoke(Object reader, Object leftparen) { PushbackReader r = (PushbackReader) reader; int line = -1; + int column = -1; if(r instanceof LineNumberingPushbackReader) + { line = ((LineNumberingPushbackReader) r).getLineNumber(); + column = ((LineNumberingPushbackReader) r).getColumnNumber(); + } List list = readDelimitedList(')', r, true); if(list.isEmpty()) return PersistentList.EMPTY; IObj s = (IObj) PersistentList.create(list); // IObj s = (IObj) RT.seq(list); if(line != -1) - return s.withMeta(RT.map(RT.LINE_KEY, line)); + { + return s.withMeta(RT.map(RT.LINE_KEY, line, RT.COLUMN_KEY, column)); + } else return s; } diff --git a/src/jvm/clojure/lang/RT.java b/src/jvm/clojure/lang/RT.java index 9042077..c0cd3fd 100644 --- a/src/jvm/clojure/lang/RT.java +++ b/src/jvm/clojure/lang/RT.java @@ -187,6 +187,7 @@ final static public Var DEFAULT_DATA_READERS = Var.intern(CLOJURE_NS, Symbol.int final static public Var ASSERT = Var.intern(CLOJURE_NS, Symbol.intern("*assert*"), T).setDynamic(); final static public Var MATH_CONTEXT = Var.intern(CLOJURE_NS, Symbol.intern("*math-context*"), null).setDynamic(); static Keyword LINE_KEY = Keyword.intern(null, "line"); +static Keyword COLUMN_KEY = Keyword.intern(null, "column"); static Keyword FILE_KEY = Keyword.intern(null, "file"); static Keyword DECLARED_KEY = Keyword.intern(null, "declared"); static Keyword DOC_KEY = Keyword.intern(null, "doc"); @@ -1670,6 +1671,12 @@ static public int getLineNumber(Reader r){ return 0; } +static public int getColumnNumber(Reader r){ + if(r instanceof LineNumberingPushbackReader) + return ((LineNumberingPushbackReader) r).getColumnNumber(); + return 0; +} + static public LineNumberingPushbackReader getLineNumberingReader(Reader r){ if(isLineNumberingReader(r)) return (LineNumberingPushbackReader) r; diff --git a/test/clojure/test_clojure/compilation.clj b/test/clojure/test_clojure/compilation.clj index f8b27de..751aa7d 100644 --- a/test/clojure/test_clojure/compilation.clj +++ b/test/clojure/test_clojure/compilation.clj @@ -34,6 +34,9 @@ (integer? (:line m)) true (> (:line m) 0) true + (integer? (:column m)) true + (> (:column m) 0) true + (:macro m) true (:name m) 'when ))) diff --git a/test/clojure/test_clojure/rt.clj b/test/clojure/test_clojure/rt.clj index a70df2d..2973e97 100644 --- a/test/clojure/test_clojure/rt.clj +++ b/test/clojure/test_clojure/rt.clj @@ -32,19 +32,19 @@ (defn prefers [] (throw (RuntimeException. "rebound!"))))) (testing "reflection cannot resolve field" (should-print-err-message - #"Reflection warning, .*:\d+ - reference to field blah can't be resolved\.\r?\n" + #"Reflection warning, .*:\d+:\d+ - reference to field blah can't be resolved\.\r?\n" (defn foo [x] (.blah x)))) (testing "reflection cannot resolve instance method" (should-print-err-message - #"Reflection warning, .*:\d+ - call to zap can't be resolved\.\r?\n" + #"Reflection warning, .*:\d+:\d+ - call to zap can't be resolved\.\r?\n" (defn foo [x] (.zap x 1)))) (testing "reflection cannot resolve static method" (should-print-err-message - #"Reflection warning, .*:\d+ - call to valueOf can't be resolved\.\r?\n" + #"Reflection warning, .*:\d+:\d+ - call to valueOf can't be resolved\.\r?\n" (defn foo [] (Integer/valueOf #"boom")))) (testing "reflection cannot resolve constructor" (should-print-err-message - #"Reflection warning, .*:\d+ - call to java.lang.String ctor can't be resolved\.\r?\n" + #"Reflection warning, .*:\d+:\d+ - call to java.lang.String ctor can't be resolved\.\r?\n" (defn foo [] (String. 1 2 3))))) (def example-var) -- 1.7.9.1