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