diff --git a/CHANGES b/CHANGES index 13c76d37..bac5abc2 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,8 @@ Changes in JMurmel-1.5.0 relative to JMurmel-1.4.8 error to jerror * compiler allows defmacro inside labels forms + * added fresh-line + * mlib: added format, formatter, error diff --git a/lambda/src/main/java/io/github/jmurmel/LambdaJ.java b/lambda/src/main/java/io/github/jmurmel/LambdaJ.java index 115d6da5..3a5eec7f 100644 --- a/lambda/src/main/java/io/github/jmurmel/LambdaJ.java +++ b/lambda/src/main/java/io/github/jmurmel/LambdaJ.java @@ -343,6 +343,7 @@ public interface ObjectWriter { default void printObj(Object o) { printObj(o, true); } default void printString(CharSequence s) { printObj(s, false); } void printEol(); + default boolean freshLine() { printEol(); return true; } } /** if an atom implements this interface then {@link Writeable#printSEx(LambdaJ.WriteConsumer, boolean)} will be used by the Murmel primitive {@code write} */ @@ -1210,14 +1211,36 @@ static ConsCell makeFeatureList(SymbolTable s) { /** this class will write objects as S-expressions to the given {@link WriteConsumer} w/o any eol translation */ private static final class SExpressionWriter implements ObjectWriter { - private final @NotNull WriteConsumer out; + private static class ColumnCountingWriteConsumer implements WriteConsumer { + private final @NotNull WriteConsumer wrapped; + int col; + + ColumnCountingWriteConsumer(@NotNull WriteConsumer wrapped) { this.wrapped = wrapped; } + + @Override public void print(CharSequence s) { + wrapped.print(s); + if (s.length() != 0) { + int pos; + for (pos = s.length() - 1; pos >= 0; --pos) { + if (s.charAt(pos) == '\n' || s.charAt(pos) == '\r') { col = 0; break; } + } + col += s.length() - 1 - pos; + } + } + } + private final @NotNull ColumnCountingWriteConsumer out; SExpressionWriter(@NotNull WriteConsumer out) { //noinspection ConstantConditions - assert out != null; this.out = out; } + assert out != null; this.out = new ColumnCountingWriteConsumer(out); } @Override public void printObj(Object o, boolean printEscape) { printSEx(out, o, printEscape); } - @Override public void printEol() { out.print("\n"); } @Override public void printString(CharSequence s) { out.print(s); } + + @Override public void printEol() { out.print("\n"); } + @Override public boolean freshLine() { + if (out.col != 0) { out.print("\n"); return true; } + return false; + } } @@ -2491,6 +2514,8 @@ enum WellknownSymbol { sWrite("write", Features.HAVE_IO, 1, 3) { @Override Object apply(LambdaJ intp, ConsCell args) { return write(intp.getLispPrinter(args, 2, intp.getLispPrinter()), car(args), cdr(args) == null || cadr(args) != null); } }, sWriteln("writeln", Features.HAVE_IO, 0, 3) { @Override Object apply(LambdaJ intp, ConsCell args) { return writeln(intp.getLispPrinter(args, 2, intp.getLispPrinter()), args, cdr(args) == null || cadr(args) != null); } }, sLnwrite("lnwrite", Features.HAVE_IO, 0, 3) { @Override Object apply(LambdaJ intp, ConsCell args) { return lnwrite(intp.getLispPrinter(args, 2, intp.getLispPrinter()), args, cdr(args) == null || cadr(args) != null); } }, + sFreshLine("fresh-line", Features.HAVE_IO, 0, 1) { @Override Object apply(LambdaJ intp, ConsCell args) { return intp.boolResult(freshLine(intp.getLispPrinter(args, 0, intp.getLispPrinter()))); } }, + sJFormat("jformat", Features.HAVE_UTIL, 2, -1) { @Override Object apply(LambdaJ intp, ConsCell args) { return Subr.jformat(intp.getLispPrinter(args, 0, null), intp.have(Features.HAVE_IO), args); } }, sJFormatLocale("jformat-locale", Features.HAVE_UTIL,3,-1) { @Override Object apply(LambdaJ intp, ConsCell args) { return jformatLocale(intp.getLispPrinter(args, 0, null), intp.have(Features.HAVE_IO), args); } }, @@ -6149,6 +6174,11 @@ static Object writeln(ObjectWriter lispPrinter) { return null; } + static boolean freshLine(ObjectWriter lispPrinter) { + if (lispPrinter == null) throw errorUnsupported("fresh-line", "%s: lispStdout is " + NIL); + return lispPrinter.freshLine(); + } + static Object lnwrite(ObjectWriter lispPrinter, ConsCell arg, boolean printEscape) { if (lispPrinter == null) throw errorUnsupported("lnwrite", "%s: lispStdout is " + NIL); lispPrinter.printEol(); @@ -8778,6 +8808,9 @@ private CompilerIteratorGenerator scanHashCompiler(Object hash) { public final Object writelnStdout () { clrValues(); return LambdaJ.Subr.writeln(lispPrinter); } public final Object writelnStdout (Object arg) { clrValues(); return LambdaJ.Subr.writeln(lispPrinter, arg); } + public final Object freshLine (Object... args) { clrValues(); varargsMinMax("fresh-line", args, 0, 1); return bool(LambdaJ.Subr.freshLine(getLispPrinter(args, 0, lispPrinter))); } + public final Object freshLine () { clrValues(); return bool(LambdaJ.Subr.freshLine(lispPrinter)); } + public final Object _lnwrite (Object... args) { clrValues(); varargsMinMax("lnwrite", args, 0, 3); return LambdaJ.Subr.lnwrite(getLispPrinter(args, 2, lispPrinter), arraySlice(args), noSecondArgOrNotNull(args)); } public final Object jformat (Object... args) { clrValues(); varargs2("jformat", args); return LambdaJ.Subr.jformat(getLispPrinter(args, 0, null), true, arraySlice(args)); } @@ -9615,6 +9648,7 @@ protected static void main(MurmelJavaProgram program) { case "write-to-string": return (CompilerPrimitive)this::writeToString; case "write": return (CompilerPrimitive)this::_write; case "writeln": return (CompilerPrimitive)this::_writeln; + case "fresh-line": return (CompilerPrimitive)this::freshLine; case "lnwrite": return (CompilerPrimitive)this::_lnwrite; case "jformat": return (CompilerPrimitive)this::jformat; @@ -9840,6 +9874,7 @@ private static void notAPrimitive(String func, Object symbol, String javaName) { + "=@numbereq" + "\n" + "<=@le" + "\n" + "<@lt" + "\n" + ">=@ge" + "\n" + ">@gt" + "\n" + "/=@ne" + "\n" + "1+@inc" + "\n" + "1-@dec" + "\n" + "read-from-string@readFromStr" + "\n" + "read-textfile-lines@readTextfileLines" + "\n" + "read-textfile@readTextfile" + "\n" + + "fresh-line@freshLine" + "\n" + "write-textfile-lines@writeTextfileLines" + "\n" + "write-textfile@writeTextfile" + "\n" + "write-to-string@writeToString" + "\n" + "jformat@jformat" + "\n" + "jformat-locale@jformatLocale" + "\n" + "char-code@charInt" + "\n" + "code-char@intChar" + "\n" + "string=@stringeq" + "\n" + "string->list@stringToList" + "\n" + "list->string@listToString" + "\n" + ADJUSTABLE_ARRAY_P+"@adjustableArrayP" + "\n" + "vector-add@vectorAdd" + "\n" + "vector-remove@vectorRemove" + "\n" @@ -11841,6 +11876,11 @@ private boolean opencode(WrappingWriter sb, LambdaJSymbol op, ConsCell args, Con return true; } } + break; + } + case sFreshLine: { + if (args == null) { sb.append("freshLine()"); return true; } + break; } case sInc: { if (consp(car(args)) && caar(args) == intern("1+")) { diff --git a/murmel-langref.html b/murmel-langref.html index bfd5e58e..0be4bf81 100644 --- a/murmel-langref.html +++ b/murmel-langref.html @@ -1274,6 +1274,13 @@ (writeln "Hello, World!" nil) +### (fresh-line [dest]) -> boolean + +Since: 1.5 + +Print an EOL character (-sequence) unless already at the beginning of line. + + ### (write-to-string obj [print-escape-p]) -> result-string Since: 1.4 diff --git a/murmel-langref.lisp b/murmel-langref.lisp index b39ca75a..ceb19344 100644 --- a/murmel-langref.lisp +++ b/murmel-langref.lisp @@ -1268,6 +1268,13 @@ pi ; ==> 3.141592653589793 (writeln "Hello, World!" nil) +; = (fresh-line [dest]) -> boolean +; +; Since: 1.5 +; +; Print an EOL character (-sequence) unless already at the beginning of line. + + ; = (write-to-string obj [print-escape-p]) -> result-string ; ; Since: 1.4 diff --git a/murmel-langref.md b/murmel-langref.md index 9477d80c..e6ba64c7 100644 --- a/murmel-langref.md +++ b/murmel-langref.md @@ -1265,6 +1265,13 @@ the optioal parameter `print-escape-p` can be used to turn off escaping: (writeln "Hello, World!" nil) +### (fresh-line [dest]) -> boolean + +Since: 1.5 + +Print an EOL character (-sequence) unless already at the beginning of line. + + ### (write-to-string obj [print-escape-p]) -> result-string Since: 1.4 diff --git a/murmel.completions b/murmel.completions index efd2dfc3..def234db 100644 --- a/murmel.completions +++ b/murmel.completions @@ -190,6 +190,7 @@ jproxy read write writeln +fresh-line lnwrite jformat jformat-locale