diff --git a/src/kiss/Macros.hx b/src/kiss/Macros.hx index 6150aa2a..6bbdb985 100644 --- a/src/kiss/Macros.hx +++ b/src/kiss/Macros.hx @@ -149,30 +149,50 @@ class Macros { CallExp(Symbol("defun").withPosOf(wholeExp), exps).withPosOf(wholeExp); } - // For now, reader macros only support a one-expression body implemented in #|raw haxe|# macros["defreadermacro"] = (wholeExp:ReaderExp, exps:Array, k:KissState) -> { - wholeExp.checkNumArgs(3, 3, '(defreadermacro "[startingString]" [[streamArgName]] [RawHaxe])'); - switch (exps[0].def) { + wholeExp.checkNumArgs(3, 3, '(defreadermacro ["[startingString]" or [startingStrings...]] [[streamArgName]] [RawHaxe])'); + + // reader macros can define a list of strings that will trigger the macro. When there are multiple, + // the macro will put back the initiating string into the stream so you can check which one it was + var stringsThatMatch = switch (exps[0].def) { case StrExp(s): - switch (exps[1].def) { - case ListExp([{pos: _, def: Symbol(streamArgName)}]): - switch (exps[2].def) { - case RawHaxe(code): - k.readTable[s] = (stream) -> { - var parser = new Parser(); - var interp = new Interp(); - interp.variables.set("ReaderExp", ReaderExpDef); - interp.variables.set(streamArgName, stream); - interp.execute(parser.parseString(code)); - }; + [s]; + case ListExp(strings): + [ + for (s in strings) + switch (s.def) { + case StrExp(s): + s; default: - throw CompileError.fromExp(exps[2], 'third argument to defreadermacro should be #|raw haxe|#'); + throw CompileError.fromExp(s, 'initiator list of defreadermacro must only contain strings'); } - default: - throw CompileError.fromExp(exps[1], 'second argument to defreadermacro should be [steamArgName]'); - } + ]; default: - throw CompileError.fromExp(exps[0], 'first argument to defreadermacro should be a String'); + throw CompileError.fromExp(exps[0], 'first argument to defreadermacro should be a String or list of strings'); + }; + + for (s in stringsThatMatch) { + switch (exps[1].def) { + case ListExp([{pos: _, def: Symbol(streamArgName)}]): + // For now, reader macros only support a one-expression body implemented in #|raw haxe|# (which can contain a block). + switch (exps[2].def) { + case RawHaxe(code): + k.readTable[s] = (stream) -> { + if (stringsThatMatch.length > 1) { + stream.putBackString(s); + } + var parser = new Parser(); + var interp = new Interp(); + interp.variables.set("ReaderExp", ReaderExpDef); + interp.variables.set(streamArgName, stream); + interp.execute(parser.parseString(code)); + }; + default: + throw CompileError.fromExp(exps[2], 'third argument to defreadermacro should be #|raw haxe|#'); + } + default: + throw CompileError.fromExp(exps[1], 'second argument to defreadermacro should be [steamArgName]'); + } } return null; diff --git a/src/test/cases/ReaderMacroTestCase.hx b/src/test/cases/ReaderMacroTestCase.hx index 007ac6bc..3e2efdc5 100644 --- a/src/test/cases/ReaderMacroTestCase.hx +++ b/src/test/cases/ReaderMacroTestCase.hx @@ -13,4 +13,10 @@ class ReaderMacroTestCase extends Test { function testDefAlias() { Assert.equals(9, ReaderMacroTestCase.mySum); } + + function testMultipleInitiators() { + Assert.equals("a", ReaderMacroTestCase.str1); + Assert.equals("b", ReaderMacroTestCase.str2); + Assert.equals("c", ReaderMacroTestCase.str3); + } } diff --git a/src/test/cases/ReaderMacroTestCase.kiss b/src/test/cases/ReaderMacroTestCase.kiss index 0968114c..48d064c1 100644 --- a/src/test/cases/ReaderMacroTestCase.kiss +++ b/src/test/cases/ReaderMacroTestCase.kiss @@ -8,4 +8,11 @@ (defalias fluffers 5) (defalias buffers 4) -(defvar mySum (pluppers fluffers buffers)) \ No newline at end of file +(defvar mySum (pluppers fluffers buffers)) + +// Read a b c directly as strings +(defreadermacro ["a" "b" "c"] [stream] #|ReaderExp.StrExp(stream.expect("a, b, or c", function () stream.takeChars(1)))|#) + +(defvar str1 a) +(defvar str2 b) +(defvar str3 c) \ No newline at end of file