Add positions to ReaderExp
This commit is contained in:
@@ -5,11 +5,13 @@ import haxe.macro.Context;
|
||||
import kiss.Reader;
|
||||
import kiss.Types;
|
||||
import kiss.Helpers;
|
||||
import kiss.Stream;
|
||||
|
||||
using kiss.Reader;
|
||||
using StringTools;
|
||||
|
||||
// Field forms convert Kiss reader expressions into Haxe macro class fields
|
||||
typedef FieldFormFunction = (position:String, args:Array<ReaderExp>, convert:ExprConversion) -> Field;
|
||||
typedef FieldFormFunction = (pos:Position, args:Array<ReaderExp>, convert:ExprConversion) -> Field;
|
||||
|
||||
class FieldForms {
|
||||
public static function builtins() {
|
||||
@@ -33,27 +35,27 @@ class FieldForms {
|
||||
return access;
|
||||
}
|
||||
|
||||
static function fieldName(formName:String, position:String, nameExp:ReaderExp) {
|
||||
return switch (nameExp) {
|
||||
case Symbol(name) | TypedExp(_, Symbol(name)):
|
||||
static function fieldName(formName:String, nameExp:ReaderExp) {
|
||||
return switch (nameExp.def) {
|
||||
case Symbol(name) | TypedExp(_, {pos: _, def: Symbol(name)}):
|
||||
name;
|
||||
default:
|
||||
throw 'The first argument to $formName at $position should be a variable name or typed variable name';
|
||||
throw 'The first argument to $formName at ${nameExp.pos} should be a variable name or typed variable name';
|
||||
};
|
||||
}
|
||||
|
||||
static function varOrProperty(formName:String, position:String, args:Array<ReaderExp>, convert:ExprConversion):Field {
|
||||
static function varOrProperty(formName:String, position:Position, args:Array<ReaderExp>, convert:ExprConversion):Field {
|
||||
if (args.length != 2) {
|
||||
throw '$formName with $args at $position is not a valid field definition';
|
||||
}
|
||||
|
||||
var name = fieldName(formName, position, args[0]);
|
||||
var name = fieldName(formName, args[0]);
|
||||
var access = fieldAccess(formName, name);
|
||||
|
||||
return {
|
||||
name: name,
|
||||
access: access,
|
||||
kind: FVar(switch (args[0]) {
|
||||
kind: FVar(switch (args[0].def) {
|
||||
case TypedExp(type, _):
|
||||
Helpers.parseComplexType(type);
|
||||
default: null;
|
||||
@@ -63,12 +65,12 @@ class FieldForms {
|
||||
}
|
||||
|
||||
// TODO &rest, &body and &optional arguments
|
||||
static function funcOrMethod(formName:String, position:String, args:Array<ReaderExp>, convert:ExprConversion):Field {
|
||||
static function funcOrMethod(formName:String, position:Position, args:Array<ReaderExp>, convert:ExprConversion):Field {
|
||||
if (args.length <= 2) {
|
||||
throw '$formName with $args is not a valid function/method definition';
|
||||
}
|
||||
|
||||
var name = fieldName(formName, position, args[0]);
|
||||
var name = fieldName(formName, args[0]);
|
||||
var access = fieldAccess(formName, name);
|
||||
|
||||
return {
|
||||
@@ -76,19 +78,19 @@ class FieldForms {
|
||||
access: access,
|
||||
// TODO type parameter declarations
|
||||
kind: FFun({
|
||||
args: switch (args[1]) {
|
||||
args: switch (args[1].def) {
|
||||
case ListExp(funcArgs):
|
||||
[
|
||||
// TODO optional arguments, default values
|
||||
for (funcArg in funcArgs)
|
||||
{
|
||||
name: switch (funcArg) {
|
||||
case Symbol(name) | TypedExp(_, Symbol(name)):
|
||||
name: switch (funcArg.def) {
|
||||
case Symbol(name) | TypedExp(_, {pos: _, def: Symbol(name)}):
|
||||
name;
|
||||
default:
|
||||
throw '$funcArg should be a symbol or typed symbol for a function argument';
|
||||
},
|
||||
type: switch (funcArg) {
|
||||
type: switch (funcArg.def) {
|
||||
case TypedExp(type, _):
|
||||
Helpers.parseComplexType(type);
|
||||
default: null;
|
||||
@@ -100,13 +102,13 @@ class FieldForms {
|
||||
default:
|
||||
throw '${args[1]} should be an argument list';
|
||||
},
|
||||
ret: switch (args[0]) {
|
||||
ret: switch (args[0].def) {
|
||||
case TypedExp(type, _): Helpers.parseComplexType(type);
|
||||
default: null;
|
||||
},
|
||||
expr: {
|
||||
pos: Context.currentPos(),
|
||||
expr: EReturn(convert(CallExp(Symbol("begin"), args.slice(2))))
|
||||
expr: EReturn(convert(CallExp(Symbol("begin").withPos(args[2].pos), args.slice(2)).withPos(args[2].pos)))
|
||||
}
|
||||
}),
|
||||
pos: Context.currentPos()
|
||||
|
||||
@@ -62,17 +62,17 @@ class Kiss {
|
||||
return classFields;
|
||||
}
|
||||
|
||||
static function readerExpToField(exp:ReaderExp, position:String, k:KissState):Null<Field> {
|
||||
static function readerExpToField(exp:ReaderExp, position:Position, k:KissState):Null<Field> {
|
||||
var fieldForms = k.fieldForms;
|
||||
|
||||
// Macros at top-level are allowed if they expand into a fieldform, or null like defreadermacro
|
||||
var macros = k.macros;
|
||||
|
||||
return switch (exp) {
|
||||
case CallExp(Symbol(mac), args) if (macros.exists(mac)):
|
||||
return switch (exp.def) {
|
||||
case CallExp({pos: _, def: Symbol(mac)}, args) if (macros.exists(mac)):
|
||||
var expandedExp = macros[mac](args, k);
|
||||
if (expandedExp != null) readerExpToField(macros[mac](args, k), position, k) else null;
|
||||
case CallExp(Symbol(formName), args) if (fieldForms.exists(formName)):
|
||||
case CallExp({pos: _, def: Symbol(formName)}, args) if (fieldForms.exists(formName)):
|
||||
fieldForms[formName](position, args, readerExpToHaxeExpr.bind(_, k));
|
||||
default:
|
||||
throw '$exp at $position is not a valid field form';
|
||||
@@ -84,7 +84,7 @@ class Kiss {
|
||||
var specialForms = k.specialForms;
|
||||
// Bind the table arguments of this function for easy recursive calling/passing
|
||||
var convert = readerExpToHaxeExpr.bind(_, k);
|
||||
var expr = switch (exp) {
|
||||
var expr = switch (exp.def) {
|
||||
case Symbol(name):
|
||||
Context.parse(name, Context.currentPos());
|
||||
case StrExp(s):
|
||||
@@ -92,9 +92,9 @@ class Kiss {
|
||||
pos: Context.currentPos(),
|
||||
expr: EConst(CString(s))
|
||||
};
|
||||
case CallExp(Symbol(mac), args) if (macros.exists(mac)):
|
||||
case CallExp({pos: _, def: Symbol(mac)}, args) if (macros.exists(mac)):
|
||||
convert(macros[mac](args, k));
|
||||
case CallExp(Symbol(specialForm), args) if (specialForms.exists(specialForm)):
|
||||
case CallExp({pos: _, def: Symbol(specialForm)}, args) if (specialForms.exists(specialForm)):
|
||||
specialForms[specialForm](args, convert);
|
||||
case CallExp(func, body):
|
||||
{
|
||||
|
||||
@@ -7,6 +7,7 @@ import hscript.Interp;
|
||||
import kiss.Reader;
|
||||
import kiss.Kiss;
|
||||
|
||||
using kiss.Reader;
|
||||
using kiss.Helpers;
|
||||
|
||||
// Macros generate new Kiss reader expressions from the arguments of their call expression.
|
||||
@@ -28,14 +29,14 @@ class Macros {
|
||||
if (exps.length != 2) {
|
||||
throw 'Got ${exps.length} arguments for % instead of 2.';
|
||||
}
|
||||
CallExp(Symbol("Prelude.mod"), [exps[1], exps[0]]);
|
||||
CallExp(Symbol("Prelude.mod").withPos(exps[0].pos), [exps[1], exps[0]]).withPos(exps[0].pos);
|
||||
};
|
||||
|
||||
macros["^"] = (exps:Array<ReaderExp>, k) -> {
|
||||
if (exps.length != 2) {
|
||||
throw 'Got ${exps.length} arguments for ^ instead of 2.';
|
||||
}
|
||||
CallExp(Symbol("Prelude.pow"), [exps[1], exps[0]]);
|
||||
CallExp(Symbol("Prelude.pow").withPos(exps[0].pos), [exps[1], exps[0]]).withPos(exps[0].pos);
|
||||
};
|
||||
|
||||
macros["min"] = foldMacro("Prelude.minInclusive");
|
||||
@@ -54,7 +55,7 @@ class Macros {
|
||||
macros["defmacrofun"] = (exps:Array<ReaderExp>, k:KissState) -> {
|
||||
if (exps.length < 3)
|
||||
throw '${exps.length} is not enough arguments for (defmacrofun [name] [args] [body])';
|
||||
var macroName = switch (exps[0]) {
|
||||
var macroName = switch (exps[0].def) {
|
||||
case Symbol(name): name;
|
||||
default: throw 'first argument ${exps[0]} for defmacrofun should be a symbol for the macro name';
|
||||
};
|
||||
@@ -69,7 +70,7 @@ class Macros {
|
||||
]).withContextPos();
|
||||
};
|
||||
|
||||
CallExp(Symbol("defun"), exps);
|
||||
CallExp(Symbol("defun").withPos(exps[0].pos), exps).withPos(exps[0].pos);
|
||||
}
|
||||
|
||||
// For now, reader macros only support a one-expression body implemented in #|raw haxe|#
|
||||
@@ -77,16 +78,16 @@ class Macros {
|
||||
if (exps.length != 3) {
|
||||
throw 'wrong number of expressions for defreadermacro: $exps should be String, [streamArgName], RawHaxe';
|
||||
}
|
||||
switch (exps[0]) {
|
||||
switch (exps[0].def) {
|
||||
case StrExp(s):
|
||||
switch (exps[1]) {
|
||||
case ListExp([Symbol(streamArgName)]):
|
||||
switch (exps[2]) {
|
||||
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", ReaderExp);
|
||||
interp.variables.set("ReaderExp", ReaderExpDef);
|
||||
interp.variables.set(streamArgName, stream);
|
||||
interp.execute(parser.parseString(code));
|
||||
};
|
||||
@@ -107,8 +108,12 @@ class Macros {
|
||||
}
|
||||
|
||||
static function foldMacro(func:String):MacroFunction {
|
||||
return (exps, k) -> {
|
||||
CallExp(Symbol("Lambda.fold"), [ListExp(exps.slice(1)), Symbol(func), exps[0]]);
|
||||
return (exps:Array<ReaderExp>, k) -> {
|
||||
CallExp(Symbol("Lambda.fold").withPos(exps[0].pos), [
|
||||
ListExp(exps.slice(1)).withPos(exps[0].pos),
|
||||
Symbol(func).withPos(exps[0].pos),
|
||||
exps[0]
|
||||
]).withPos(exps[0].pos);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,14 @@ package kiss;
|
||||
import haxe.ds.Option;
|
||||
import kiss.Stream;
|
||||
|
||||
enum ReaderExp {
|
||||
using kiss.Reader;
|
||||
|
||||
typedef ReaderExp = {
|
||||
pos:Position,
|
||||
def:ReaderExpDef
|
||||
};
|
||||
|
||||
enum ReaderExpDef {
|
||||
CallExp(func:ReaderExp, args:Array<ReaderExp>); // (f a1 a2...)
|
||||
ListExp(exps:Array<ReaderExp>); // [v1 v2 v3]
|
||||
StrExp(s:String); // "literal"
|
||||
@@ -12,7 +19,7 @@ enum ReaderExp {
|
||||
TypedExp(path:String, exp:ReaderExp); // :type [exp]
|
||||
}
|
||||
|
||||
typedef ReadFunction = (Stream) -> Null<ReaderExp>;
|
||||
typedef ReadFunction = (Stream) -> Null<ReaderExpDef>;
|
||||
|
||||
class Reader {
|
||||
// The built-in readtable
|
||||
@@ -64,6 +71,7 @@ class Reader {
|
||||
if (stream.isEmpty())
|
||||
return None;
|
||||
|
||||
var position = stream.position();
|
||||
var readTableKeys = [for (key in readTable.keys()) key];
|
||||
readTableKeys.sort((a, b) -> b.length - a.length);
|
||||
|
||||
@@ -72,7 +80,11 @@ class Reader {
|
||||
case Some(k) if (k == key):
|
||||
stream.dropString(key);
|
||||
var expOrNull = readTable[key](stream);
|
||||
return if (expOrNull != null) Some(expOrNull) else read(stream, readTable);
|
||||
return if (expOrNull != null) {
|
||||
Some(expOrNull.withPos(position));
|
||||
} else {
|
||||
read(stream, readTable);
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
@@ -90,4 +102,11 @@ class Reader {
|
||||
stream.dropString(end);
|
||||
return array;
|
||||
}
|
||||
|
||||
public static function withPos(def:ReaderExpDef, pos:Position) {
|
||||
return {
|
||||
pos: pos,
|
||||
def: def
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import haxe.macro.Context;
|
||||
import kiss.Reader;
|
||||
import kiss.Types;
|
||||
|
||||
using kiss.Reader;
|
||||
using kiss.Helpers;
|
||||
using kiss.Prelude;
|
||||
|
||||
@@ -33,7 +34,7 @@ class SpecialForms {
|
||||
if (args.length < 1) {
|
||||
throw '(new [type] constructorArgs...) is missing a type!';
|
||||
}
|
||||
var classType = switch (args[0]) {
|
||||
var classType = switch (args[0].def) {
|
||||
case Symbol(name): name;
|
||||
default: throw 'first arg in (new [type] ...) should be a class to instantiate';
|
||||
};
|
||||
@@ -46,7 +47,7 @@ class SpecialForms {
|
||||
|
||||
// TODO refactor out EVar generation and allow var bindings to destructure lists and key-value pairs
|
||||
map["let"] = (args:Array<ReaderExp>, convert:ExprConversion) -> {
|
||||
var bindingList = switch (args[0]) {
|
||||
var bindingList = switch (args[0].def) {
|
||||
case ListExp(bindingExps) if (bindingExps.length > 0 && bindingExps.length % 2 == 0):
|
||||
bindingExps;
|
||||
default:
|
||||
@@ -56,13 +57,13 @@ class SpecialForms {
|
||||
var varDefs = [
|
||||
for (bindingPair in bindingPairs)
|
||||
{
|
||||
name: switch (bindingPair[0]) {
|
||||
case Symbol(name) | TypedExp(_, Symbol(name)):
|
||||
name: switch (bindingPair[0].def) {
|
||||
case Symbol(name) | TypedExp(_, {pos: _, def: Symbol(name)}):
|
||||
name;
|
||||
default:
|
||||
throw 'first element of binding pair $bindingPair should be a symbol or typed symbol';
|
||||
},
|
||||
type: switch (bindingPair[0]) {
|
||||
type: switch (bindingPair[0].def) {
|
||||
case TypedExp(type, _):
|
||||
Helpers.parseComplexType(type);
|
||||
default: null;
|
||||
@@ -97,7 +98,7 @@ class SpecialForms {
|
||||
if (args.length != 2) {
|
||||
throw '(the [type] [value]) expression has wrong number of arguments: ${args.length}';
|
||||
}
|
||||
ECheckType(convert(args[1]), switch (args[0]) {
|
||||
ECheckType(convert(args[1]), switch (args[0].def) {
|
||||
case Symbol(type): Helpers.parseComplexType(type);
|
||||
default: throw 'first argument to (the... ) should be a valid type';
|
||||
}).withContextPos();
|
||||
@@ -111,19 +112,24 @@ class SpecialForms {
|
||||
var catchKissExps = args.slice(1);
|
||||
ETry(convert(tryKissExp), [
|
||||
for (catchKissExp in catchKissExps) {
|
||||
switch (catchKissExp) {
|
||||
case CallExp(Symbol("catch"), catchArgs):
|
||||
switch (catchKissExp.def) {
|
||||
case CallExp({pos: _, def: Symbol("catch")}, catchArgs):
|
||||
{
|
||||
name: switch (catchArgs[0]) {
|
||||
case ListExp([Symbol(name) | TypedExp(_, Symbol(name))]): name;
|
||||
name: switch (catchArgs[0].def) {
|
||||
case ListExp([
|
||||
{
|
||||
pos: _,
|
||||
def: Symbol(name) | TypedExp(_, {pos: _, def: Symbol(name)})
|
||||
}
|
||||
]): name;
|
||||
default: throw 'first argument to (catch... ) should be a one-element argument list, not ${catchArgs[0]}';
|
||||
},
|
||||
type: switch (catchArgs[0]) {
|
||||
case ListExp([TypedExp(type, _)]):
|
||||
type: switch (catchArgs[0].def) {
|
||||
case ListExp([{pos: _, def: TypedExp(type, _)}]):
|
||||
Helpers.parseComplexType(type);
|
||||
default: null;
|
||||
},
|
||||
expr: convert(CallExp(Symbol("begin"), catchArgs.slice(1)))
|
||||
expr: convert(CallExp(Symbol("begin").withPos(catchArgs[1].pos), catchArgs.slice(1)).withPos(catchArgs[1].pos))
|
||||
};
|
||||
default:
|
||||
throw 'expressions following the first expression in a (try... ) should all be (catch... ) expressions, but you used $catchKissExp';
|
||||
@@ -165,7 +171,7 @@ class SpecialForms {
|
||||
static function foldComparison(func:String) {
|
||||
return (args:Array<ReaderExp>, convert:ExprConversion) -> {
|
||||
pos: Context.currentPos(),
|
||||
expr: EBinop(OpEq, convert(args[0]), convert(CallExp(Symbol(func), args)))
|
||||
expr: EBinop(OpEq, convert(args[0]), convert(CallExp(Symbol(func).withPos(args[0].pos), args).withPos(args[0].pos)))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import haxe.ds.Option;
|
||||
using StringTools;
|
||||
using Lambda;
|
||||
|
||||
typedef Position = String;
|
||||
|
||||
class Stream {
|
||||
var content:String;
|
||||
var file:String;
|
||||
@@ -34,7 +36,7 @@ class Stream {
|
||||
return content.length == 0;
|
||||
}
|
||||
|
||||
public function position() {
|
||||
public function position():Position {
|
||||
return '$file:$line:$column';
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user