diff --git a/kiss/src/kiss/Kiss.hx b/kiss/src/kiss/Kiss.hx index e69c5207..8e5bc429 100644 --- a/kiss/src/kiss/Kiss.hx +++ b/kiss/src/kiss/Kiss.hx @@ -97,6 +97,10 @@ class Kiss { Sys.println(err); Sys.exit(1); return null; // Necessary for build() to compile + } catch (err:UnmatchedBracketSignal) { + Sys.println(Stream.toPrint(err.position) + ': Unmatched ${err.type}'); + Sys.exit(1); + return null; } catch (err:Exception) { throw err; // Re-throw haxe exceptions for precise stacks } diff --git a/kiss/src/kiss/Reader.hx b/kiss/src/kiss/Reader.hx index d5074c68..7fb7e0e9 100644 --- a/kiss/src/kiss/Reader.hx +++ b/kiss/src/kiss/Reader.hx @@ -10,6 +10,16 @@ typedef ReaderExp = { def:ReaderExpDef }; +class UnmatchedBracketSignal { + public var type:String; + public var position:Stream.Position; + + public function new(type, position) { + this.type = type; + this.position = position; + } +} + enum ReaderExpDef { CallExp(func:ReaderExp, args:Array); // (f a1 a2...) ListExp(exps:Array); // [v1 v2 v3] @@ -61,9 +71,17 @@ class Reader { // Lets you construct key-value pairs for map literals or for-loops readTable["=>"] = (stream:Stream) -> KeyValueExp(assertRead(stream, readTable), assertRead(stream, readTable)); + readTable[")"] = (stream:Stream) -> { + stream.putBackString(")"); + throw new UnmatchedBracketSignal(")", stream.position()); + }; + readTable["]"] = (stream:Stream) -> { + stream.putBackString("]"); + throw new UnmatchedBracketSignal("]", stream.position()); + }; + // Because macro keys are sorted by length and peekChars(0) returns "", this will be used as the default reader macro: readTable[""] = (stream) -> Symbol(nextToken(stream, "a symbol name")); - // TODO make - A macro for numerical negation return readTable; } @@ -115,8 +133,16 @@ class Reader { var array = []; while (!stream.startsWith(end)) { stream.dropWhitespace(); - if (!stream.startsWith(end)) - array.push(assertRead(stream, readTable)); + if (!stream.startsWith(end)) { + try { + array.push(assertRead(stream, readTable)); + } catch (s:UnmatchedBracketSignal) { + if (s.type == end) + break; + else + throw s; + } + } } stream.dropString(end); return array; diff --git a/kiss/src/test/cases/CommentAtEndOfListTestCase.hx b/kiss/src/test/cases/CommentAtEndOfListTestCase.hx new file mode 100644 index 00000000..4923ec1e --- /dev/null +++ b/kiss/src/test/cases/CommentAtEndOfListTestCase.hx @@ -0,0 +1,9 @@ +package test.cases; + +import utest.Assert; +import utest.Test; +import kiss.Prelude; +import kiss.List; + +@:build(kiss.Kiss.build("kiss/src/test/cases/CommentAtEndOfListTestCase.kiss")) +class CommentAtEndOfListTestCase extends Test {} diff --git a/kiss/src/test/cases/CommentAtEndOfListTestCase.kiss b/kiss/src/test/cases/CommentAtEndOfListTestCase.kiss index b1549d52..9562c23f 100644 --- a/kiss/src/test/cases/CommentAtEndOfListTestCase.kiss +++ b/kiss/src/test/cases/CommentAtEndOfListTestCase.kiss @@ -1,4 +1,4 @@ (defun myFun [] (deflocal something 5) - // TODO This comment causes a hard-to-track-down error! + // This comment used to cause a hard-to-track-down error! ) \ No newline at end of file