Reader macros implemented in Kiss
This commit is contained in:
@@ -9,8 +9,9 @@ import kiss.Reader;
|
||||
import kiss.Kiss;
|
||||
import kiss.CompileError;
|
||||
|
||||
using kiss.Kiss;
|
||||
using tink.MacroApi;
|
||||
using uuid.Uuid;
|
||||
using kiss.Kiss;
|
||||
using kiss.Reader;
|
||||
using kiss.Helpers;
|
||||
|
||||
@@ -181,7 +182,7 @@ class Macros {
|
||||
};
|
||||
|
||||
macros["defreadermacro"] = (wholeExp:ReaderExp, exps:Array<ReaderExp>, k:KissState) -> {
|
||||
wholeExp.checkNumArgs(3, 3, '(defreadermacro ["[startingString]" or [startingStrings...]] [[streamArgName]] [RawHaxe])');
|
||||
wholeExp.checkNumArgs(3, null, '(defreadermacro ["[startingString]" or [startingStrings...]] [[streamArgName]] [body...])');
|
||||
|
||||
// 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
|
||||
@@ -205,23 +206,19 @@ class Macros {
|
||||
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();
|
||||
// TODO reader macros also need to access the readtable
|
||||
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|#');
|
||||
}
|
||||
k.readTable[s] = (stream) -> {
|
||||
if (stringsThatMatch.length > 1) {
|
||||
stream.putBackString(s);
|
||||
}
|
||||
var body = CallExp(Symbol("begin").withPos(stream.position()), exps.slice(2)).withPos(stream.position());
|
||||
var code = k.convert(body).toString(); // tink_macro to the rescue
|
||||
var parser = new Parser();
|
||||
var interp = new Interp();
|
||||
// TODO reader macros also need to access the readtable
|
||||
interp.variables.set("ReaderExp", ReaderExpDef);
|
||||
interp.variables.set(streamArgName, stream);
|
||||
interp.execute(Prelude.print(parser.parseString(code)));
|
||||
};
|
||||
default:
|
||||
throw CompileError.fromExp(exps[1], 'second argument to defreadermacro should be [steamArgName]');
|
||||
}
|
||||
|
@@ -30,6 +30,8 @@ enum ReaderExpDef {
|
||||
MetaExp(meta:String, exp:ReaderExp); // &meta [exp]
|
||||
FieldExp(field:String, exp:ReaderExp); // .field [exp]
|
||||
KeyValueExp(key:ReaderExp, value:ReaderExp); // =>key value
|
||||
Quasiquote(exp:ReaderExp); // `[exp]
|
||||
Unquote(exp:ReaderExp); // ,[exp]
|
||||
}
|
||||
|
||||
typedef ReadFunction = (Stream) -> Null<ReaderExpDef>;
|
||||
@@ -78,6 +80,9 @@ class Reader {
|
||||
throw new UnmatchedBracketSignal("]", stream.position());
|
||||
};
|
||||
|
||||
readTable["`"] = (stream) -> Quasiquote(assertRead(stream, readTable));
|
||||
readTable[","] = (stream) -> Unquote(assertRead(stream, readTable));
|
||||
|
||||
// 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"));
|
||||
|
||||
@@ -223,6 +228,10 @@ class Reader {
|
||||
'.$field ${exp.def.toString()}';
|
||||
case KeyValueExp(keyExp, valueExp):
|
||||
'=>${keyExp.def.toString()} ${valueExp.def.toString()}';
|
||||
case Quasiquote(exp):
|
||||
'`${exp.def.toString()}';
|
||||
case Unquote(exp):
|
||||
',${exp.def.toString()}';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -191,7 +191,7 @@ class SpecialForms {
|
||||
|
||||
map["lambda"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
|
||||
wholeExp.checkNumArgs(2, null, "(lambda [[argsNames...]] [body...])");
|
||||
EFunction(FArrow, Helpers.makeFunction(null, args[0], args.slice(1), k)).withMacroPosOf(wholeExp);
|
||||
EFunction(FAnonymous, Helpers.makeFunction(null, args[0], args.slice(1), k)).withMacroPosOf(wholeExp);
|
||||
};
|
||||
|
||||
function forExpr(formName:String, wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) {
|
||||
|
@@ -1,4 +1,6 @@
|
||||
(defreadermacro "!" [stream] #|ReaderExp.StrExp(stream.expect("a string line", function () stream.takeLine()))|#)
|
||||
(defreadermacro "!" [stream]
|
||||
(let [line (stream.expect "a string line" (lambda [] (stream.takeLine)))]
|
||||
(ReaderExp.StrExp line)))
|
||||
|
||||
(defun myLine []
|
||||
!String that takes the rest of the line
|
||||
|
Reference in New Issue
Block a user