Pass KissState instead of convert
This commit is contained in:
@@ -3,17 +3,17 @@ package kiss;
|
||||
import haxe.macro.Expr;
|
||||
import haxe.macro.Context;
|
||||
import kiss.Reader;
|
||||
import kiss.Types;
|
||||
import kiss.Helpers;
|
||||
import kiss.Stream;
|
||||
import kiss.CompileError;
|
||||
import kiss.Kiss;
|
||||
|
||||
using kiss.Helpers;
|
||||
using kiss.Reader;
|
||||
using StringTools;
|
||||
|
||||
// Field forms convert Kiss reader expressions into Haxe macro class fields
|
||||
typedef FieldFormFunction = (wholeExp:ReaderExp, args:Array<ReaderExp>, convert:ExprConversion) -> Field;
|
||||
typedef FieldFormFunction = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> Field;
|
||||
|
||||
class FieldForms {
|
||||
public static function builtins() {
|
||||
@@ -61,7 +61,7 @@ class FieldForms {
|
||||
};
|
||||
}
|
||||
|
||||
static function varOrProperty(formName:String, wholeExp:ReaderExp, args:Array<ReaderExp>, convert:ExprConversion):Field {
|
||||
static function varOrProperty(formName:String, wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState):Field {
|
||||
wholeExp.checkNumArgs(2, 3, '($formName [optional :type] [variable] [optional: &mut] [value])');
|
||||
|
||||
var name = fieldName(formName, args[0]);
|
||||
@@ -74,12 +74,12 @@ class FieldForms {
|
||||
case TypedExp(type, _):
|
||||
Helpers.parseComplexType(type, args[0]);
|
||||
default: null;
|
||||
}, convert(args[1])),
|
||||
}, k.convert(args[1])),
|
||||
pos: Context.currentPos()
|
||||
};
|
||||
}
|
||||
|
||||
static function funcOrMethod(formName:String, wholeExp:ReaderExp, args:Array<ReaderExp>, convert:ExprConversion):Field {
|
||||
static function funcOrMethod(formName:String, wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState):Field {
|
||||
wholeExp.checkNumArgs(3, null, '($formName [optional :type] [name] [[argNames...]] [body...])');
|
||||
|
||||
var name = fieldName(formName, args[0]);
|
||||
@@ -88,7 +88,7 @@ class FieldForms {
|
||||
return {
|
||||
name: name,
|
||||
access: access,
|
||||
kind: FFun(Helpers.makeFunction(args[0], args[1], args.slice(2), convert)),
|
||||
kind: FFun(Helpers.makeFunction(args[0], args[1], args.slice(2), k)),
|
||||
pos: Context.currentPos()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import haxe.macro.Expr;
|
||||
import haxe.macro.Context;
|
||||
import kiss.Reader;
|
||||
import kiss.CompileError;
|
||||
import kiss.Types;
|
||||
import kiss.Kiss;
|
||||
|
||||
using kiss.Reader;
|
||||
@@ -56,7 +55,8 @@ class Helpers {
|
||||
}
|
||||
|
||||
// TODO generic type parameter declarations
|
||||
public static function makeFunction(?name:ReaderExp, argList:ReaderExp, body:Array<ReaderExp>, convert:ExprConversion):Function {
|
||||
|
||||
public static function makeFunction(?name:ReaderExp, argList:ReaderExp, body:Array<ReaderExp>, k:KissState):Function {
|
||||
return {
|
||||
ret: if (name != null) switch (name.def) {
|
||||
case TypedExp(type, _): Helpers.parseComplexType(type, name);
|
||||
@@ -87,7 +87,7 @@ class Helpers {
|
||||
default:
|
||||
throw CompileError.fromExp(argList, 'expected an argument list');
|
||||
},
|
||||
expr: EReturn(convert(CallExp(Symbol("begin").withPos(body[0].pos), body).withPos(body[0].pos))).withContextPos()
|
||||
expr: EReturn(k.convert(CallExp(Symbol("begin").withPos(body[0].pos), body).withPos(body[0].pos))).withContextPos()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,13 +8,14 @@ import kiss.Reader;
|
||||
import kiss.FieldForms;
|
||||
import kiss.SpecialForms;
|
||||
import kiss.Macros;
|
||||
import kiss.Types;
|
||||
import kiss.CompileError;
|
||||
|
||||
using kiss.Helpers;
|
||||
using kiss.Reader;
|
||||
using tink.MacroApi;
|
||||
|
||||
typedef ExprConversion = (ReaderExp) -> Expr;
|
||||
|
||||
typedef KissState = {
|
||||
className:String,
|
||||
readTable:Map<String, ReadFunction>,
|
||||
@@ -90,7 +91,7 @@ class Kiss {
|
||||
var expandedExp = macros[mac](exp, args, k);
|
||||
if (expandedExp != null) readerExpToField(macros[mac](expandedExp, args, k), k) else null;
|
||||
case CallExp({pos: _, def: Symbol(formName)}, args) if (fieldForms.exists(formName)):
|
||||
fieldForms[formName](exp, args, readerExpToHaxeExpr.bind(_, k));
|
||||
fieldForms[formName](exp, args, k);
|
||||
default:
|
||||
throw CompileError.fromExp(exp, 'invalid valid field form');
|
||||
};
|
||||
@@ -112,7 +113,7 @@ class Kiss {
|
||||
case CallExp({pos: _, def: Symbol(mac)}, args) if (macros.exists(mac)):
|
||||
convert(macros[mac](exp, args, k));
|
||||
case CallExp({pos: _, def: Symbol(specialForm)}, args) if (specialForms.exists(specialForm)):
|
||||
specialForms[specialForm](exp, args, convert);
|
||||
specialForms[specialForm](exp, args, k);
|
||||
case CallExp(func, body):
|
||||
ECall(convert(func), [for (bodyExp in body) convert(bodyExp)]).withContextPos();
|
||||
case ListExp(elements):
|
||||
|
||||
@@ -3,7 +3,6 @@ package kiss;
|
||||
import haxe.macro.Expr;
|
||||
import haxe.macro.Context;
|
||||
import kiss.Reader;
|
||||
import kiss.Types;
|
||||
import uuid.Uuid;
|
||||
|
||||
using uuid.Uuid;
|
||||
@@ -11,28 +10,30 @@ using kiss.Reader;
|
||||
using kiss.Helpers;
|
||||
using kiss.Prelude;
|
||||
|
||||
import kiss.Kiss;
|
||||
|
||||
// Special forms convert Kiss reader expressions into Haxe macro expressions
|
||||
typedef SpecialFormFunction = (wholeExp:ReaderExp, args:Array<ReaderExp>, convert:ExprConversion) -> Expr;
|
||||
typedef SpecialFormFunction = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> Expr;
|
||||
|
||||
class SpecialForms {
|
||||
public static function builtins() {
|
||||
var map:Map<String, SpecialFormFunction> = [];
|
||||
|
||||
map["begin"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, convert:ExprConversion) -> {
|
||||
map["begin"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
|
||||
// Sometimes empty blocks are useful, so a checkNumArgs() seems unnecessary here for now.
|
||||
|
||||
EBlock([for (bodyExp in args) convert(bodyExp)]).withContextPos();
|
||||
EBlock([for (bodyExp in args) k.convert(bodyExp)]).withContextPos();
|
||||
};
|
||||
|
||||
map["nth"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, convert:ExprConversion) -> {
|
||||
map["nth"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
|
||||
wholeExp.checkNumArgs(2, 2, "(nth [list] [idx])");
|
||||
EArray(convert(args[0]), convert(args[1])).withContextPos();
|
||||
EArray(k.convert(args[0]), k.convert(args[1])).withContextPos();
|
||||
};
|
||||
|
||||
function makeQuickNth(idx:Int, name:String) {
|
||||
map[name] = (wholeExp:ReaderExp, args:Array<ReaderExp>, convert:ExprConversion) -> {
|
||||
map[name] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
|
||||
wholeExp.checkNumArgs(1, 1, '($name [list])');
|
||||
EArray(convert(args[0]), macro $v{idx}).withContextPos();
|
||||
EArray(k.convert(args[0]), macro $v{idx}).withContextPos();
|
||||
};
|
||||
}
|
||||
makeQuickNth(0, "first");
|
||||
@@ -51,28 +52,28 @@ class SpecialForms {
|
||||
|
||||
// TODO special form for object declaration
|
||||
|
||||
map["new"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, convert:ExprConversion) -> {
|
||||
map["new"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
|
||||
wholeExp.checkNumArgs(1, null, '(new [type] [constructorArgs...])');
|
||||
var classType = switch (args[0].def) {
|
||||
case Symbol(name): name;
|
||||
default: throw CompileError.fromExp(args[0], 'first arg in (new [type] ...) should be a class to instantiate');
|
||||
};
|
||||
ENew(Helpers.parseTypePath(classType, args[0]), args.slice(1).map(convert)).withContextPos();
|
||||
ENew(Helpers.parseTypePath(classType, args[0]), args.slice(1).map(k.convert)).withContextPos();
|
||||
};
|
||||
|
||||
map["set"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, convert:ExprConversion) -> {
|
||||
map["set"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
|
||||
wholeExp.checkNumArgs(2, 2, "(set [variable] [value])");
|
||||
EBinop(OpAssign, convert(args[0]), convert(args[1])).withContextPos();
|
||||
EBinop(OpAssign, k.convert(args[0]), k.convert(args[1])).withContextPos();
|
||||
};
|
||||
|
||||
function toVar(nameExp:ReaderExp, valueExp:ReaderExp, convert:ExprConversion, ?isFinal:Bool):Var {
|
||||
function toVar(nameExp:ReaderExp, valueExp:ReaderExp, k:KissState, ?isFinal:Bool):Var {
|
||||
// This check seems like unnecessary repetition but it's not. It allows is so that individual destructured bindings can specify mutability
|
||||
return if (isFinal == null) {
|
||||
switch (nameExp.def) {
|
||||
case MetaExp("mut", innerNameExp):
|
||||
toVar(innerNameExp, valueExp, convert, false);
|
||||
toVar(innerNameExp, valueExp, k, false);
|
||||
default:
|
||||
toVar(nameExp, valueExp, convert, true);
|
||||
toVar(nameExp, valueExp, k, true);
|
||||
};
|
||||
} else {
|
||||
name: switch (nameExp.def) {
|
||||
@@ -87,29 +88,29 @@ class SpecialForms {
|
||||
default: null;
|
||||
},
|
||||
isFinal: isFinal,
|
||||
expr: convert(valueExp)
|
||||
expr: k.convert(valueExp)
|
||||
};
|
||||
}
|
||||
|
||||
function toVars(namesExp:ReaderExp, valueExp:ReaderExp, convert:ExprConversion, ?isFinal:Bool):Array<Var> {
|
||||
function toVars(namesExp:ReaderExp, valueExp:ReaderExp, k:KissState, ?isFinal:Bool):Array<Var> {
|
||||
return if (isFinal == null) {
|
||||
switch (namesExp.def) {
|
||||
case MetaExp("mut", innerNamesExp):
|
||||
toVars(innerNamesExp, valueExp, convert, false);
|
||||
toVars(innerNamesExp, valueExp, k, false);
|
||||
default:
|
||||
toVars(namesExp, valueExp, convert, true);
|
||||
toVars(namesExp, valueExp, k, true);
|
||||
};
|
||||
} else {
|
||||
switch (namesExp.def) {
|
||||
case Symbol(_) | TypedExp(_, {pos: _, def: Symbol(_)}):
|
||||
[toVar(namesExp, valueExp, convert, isFinal)];
|
||||
[toVar(namesExp, valueExp, k, isFinal)];
|
||||
case ListExp(nameExps):
|
||||
var idx = 0;
|
||||
[
|
||||
for (nameExp in nameExps)
|
||||
toVar(nameExp,
|
||||
CallExp(Symbol("nth").withPosOf(valueExp), [valueExp, Symbol(Std.string(idx++)).withPosOf(valueExp)]).withPosOf(valueExp),
|
||||
convert, if (isFinal == false) false else null)
|
||||
k, if (isFinal == false) false else null)
|
||||
];
|
||||
default:
|
||||
throw CompileError.fromExp(namesExp, "Can only bind variables to a symbol or list of symbols for destructuring");
|
||||
@@ -117,12 +118,12 @@ class SpecialForms {
|
||||
};
|
||||
}
|
||||
|
||||
map["deflocal"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, convert:ExprConversion) -> {
|
||||
map["deflocal"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
|
||||
wholeExp.checkNumArgs(2, 3, "(deflocal [optional :type] [variable] [optional: &mut] [value])");
|
||||
EVars(toVars(args[0], args[1], convert)).withContextPos();
|
||||
EVars(toVars(args[0], args[1], k)).withContextPos();
|
||||
};
|
||||
|
||||
map["let"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, convert:ExprConversion) -> {
|
||||
map["let"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
|
||||
wholeExp.checkNumArgs(2, null, "(let [[bindings...]] [body...])");
|
||||
var bindingList = switch (args[0].def) {
|
||||
case ListExp(bindingExps) if (bindingExps.length > 0 && bindingExps.length % 2 == 0):
|
||||
@@ -133,7 +134,7 @@ class SpecialForms {
|
||||
var bindingPairs = bindingList.groups(2);
|
||||
var varDefs = [];
|
||||
for (bindingPair in bindingPairs) {
|
||||
varDefs = varDefs.concat(toVars(bindingPair[0], bindingPair[1], convert));
|
||||
varDefs = varDefs.concat(toVars(bindingPair[0], bindingPair[1], k));
|
||||
}
|
||||
|
||||
var body = args.slice(1);
|
||||
@@ -141,15 +142,15 @@ class SpecialForms {
|
||||
throw CompileError.fromArgs(args, '(let....) expression needs a body');
|
||||
}
|
||||
|
||||
EBlock([EVars(varDefs).withContextPos(), EBlock(body.map(convert)).withContextPos()]).withContextPos();
|
||||
EBlock([EVars(varDefs).withContextPos(), EBlock(body.map(k.convert)).withContextPos()]).withContextPos();
|
||||
};
|
||||
|
||||
map["lambda"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, convert:ExprConversion) -> {
|
||||
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), convert)).withContextPos();
|
||||
EFunction(FArrow, Helpers.makeFunction(null, args[0], args.slice(1), k)).withContextPos();
|
||||
};
|
||||
|
||||
function forExpr(formName:String, wholeExp:ReaderExp, args:Array<ReaderExp>, convert:ExprConversion) {
|
||||
function forExpr(formName:String, wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) {
|
||||
wholeExp.checkNumArgs(3, null, '($formName [varName] [list] [body...])');
|
||||
var uniqueVarName = "_" + Uuid.v4().toShort();
|
||||
var namesExp = args[0];
|
||||
@@ -157,30 +158,30 @@ class SpecialForms {
|
||||
var bodyExps = args.slice(2);
|
||||
bodyExps.insert(0, CallExp(Symbol("deflocal").withPosOf(args[2]), [namesExp, Symbol(uniqueVarName).withPosOf(args[2])]).withPosOf(args[2]));
|
||||
var body = CallExp(Symbol("begin").withPosOf(args[2]), bodyExps).withPosOf(args[2]);
|
||||
return EFor(EBinop(OpIn, EConst(CIdent(uniqueVarName)).withContextPos(), convert(listExp)).withContextPos(), convert(body)).withContextPos();
|
||||
return EFor(EBinop(OpIn, EConst(CIdent(uniqueVarName)).withContextPos(), k.convert(listExp)).withContextPos(), k.convert(body)).withContextPos();
|
||||
}
|
||||
|
||||
map["doFor"] = forExpr.bind("doFor");
|
||||
map["for"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, convert:ExprConversion) -> {
|
||||
EArrayDecl([forExpr("for", wholeExp, args, convert)]).withContextPos();
|
||||
map["for"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
|
||||
EArrayDecl([forExpr("for", wholeExp, args, k)]).withContextPos();
|
||||
};
|
||||
|
||||
// TODO (case... ) for switch
|
||||
|
||||
// Type check syntax:
|
||||
map["the"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, convert:ExprConversion) -> {
|
||||
map["the"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
|
||||
wholeExp.checkNumArgs(2, 2, '(the [type] [value])');
|
||||
ECheckType(convert(args[1]), switch (args[0].def) {
|
||||
ECheckType(k.convert(args[1]), switch (args[0].def) {
|
||||
case Symbol(type): Helpers.parseComplexType(type, args[0]);
|
||||
default: throw CompileError.fromExp(args[0], 'first argument to (the... ) should be a valid type');
|
||||
}).withContextPos();
|
||||
};
|
||||
|
||||
map["try"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, convert:ExprConversion) -> {
|
||||
map["try"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
|
||||
wholeExp.checkNumArgs(1, null, "(try [thing] [catches...])");
|
||||
var tryKissExp = args[0];
|
||||
var catchKissExps = args.slice(1);
|
||||
ETry(convert(tryKissExp), [
|
||||
ETry(k.convert(tryKissExp), [
|
||||
for (catchKissExp in catchKissExps) {
|
||||
switch (catchKissExp.def) {
|
||||
case CallExp({pos: _, def: Symbol("catch")}, catchArgs):
|
||||
@@ -199,7 +200,7 @@ class SpecialForms {
|
||||
Helpers.parseComplexType(type, catchArgs[0]);
|
||||
default: null;
|
||||
},
|
||||
expr: convert(CallExp(Symbol("begin").withPos(catchArgs[1].pos), catchArgs.slice(1)).withPos(catchArgs[1].pos))
|
||||
expr: k.convert(CallExp(Symbol("begin").withPos(catchArgs[1].pos), catchArgs.slice(1)).withPos(catchArgs[1].pos))
|
||||
};
|
||||
default:
|
||||
throw CompileError.fromExp(catchKissExp,
|
||||
@@ -209,11 +210,11 @@ class SpecialForms {
|
||||
]).withContextPos();
|
||||
};
|
||||
|
||||
map["throw"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, convert:ExprConversion) -> {
|
||||
map["throw"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
|
||||
if (args.length != 1) {
|
||||
throw CompileError.fromExp(wholeExp, 'throw expression should only throw one value');
|
||||
}
|
||||
EThrow(convert(args[0])).withContextPos();
|
||||
EThrow(k.convert(args[0])).withContextPos();
|
||||
};
|
||||
|
||||
map["<"] = foldComparison("_min");
|
||||
@@ -222,13 +223,13 @@ class SpecialForms {
|
||||
map[">="] = foldComparison("max");
|
||||
map["="] = foldComparison("_eq");
|
||||
|
||||
map["if"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, convert:ExprConversion) -> {
|
||||
map["if"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
|
||||
wholeExp.checkNumArgs(2, 3, '(if [cond] [then] [?else])');
|
||||
|
||||
var condition = macro Prelude.truthy(${convert(args[0])});
|
||||
var thenExp = convert(args[1]);
|
||||
var condition = macro Prelude.truthy(${k.convert(args[0])});
|
||||
var thenExp = k.convert(args[1]);
|
||||
var elseExp = if (args.length > 2) {
|
||||
convert(args[2]);
|
||||
k.convert(args[2]);
|
||||
} else {
|
||||
// Kiss (if... ) expressions all need to generate a Haxe else block
|
||||
// to make sure they always return something
|
||||
@@ -241,10 +242,10 @@ class SpecialForms {
|
||||
$elseExp;
|
||||
};
|
||||
|
||||
map["not"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, convert:ExprConversion) -> {
|
||||
map["not"] = (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
|
||||
if (args.length != 1)
|
||||
throw CompileError.fromExp(wholeExp, '(not... ) only takes one argument, not $args');
|
||||
var condition = convert(args[0]);
|
||||
var condition = k.convert(args[0]);
|
||||
var truthyExp = macro Prelude.truthy($condition);
|
||||
macro !$truthyExp;
|
||||
};
|
||||
@@ -253,9 +254,9 @@ class SpecialForms {
|
||||
}
|
||||
|
||||
static function foldComparison(func:String) {
|
||||
return (wholeExp:ReaderExp, args:Array<ReaderExp>, convert:ExprConversion) -> {
|
||||
return (wholeExp:ReaderExp, args:Array<ReaderExp>, k:KissState) -> {
|
||||
wholeExp.checkNumArgs(1, null);
|
||||
EBinop(OpEq, convert(args[0]), convert(CallExp(Symbol(func).withPosOf(wholeExp), args).withPosOf(wholeExp))).withContextPos();
|
||||
EBinop(OpEq, k.convert(args[0]), k.convert(CallExp(Symbol(func).withPosOf(wholeExp), args).withPosOf(wholeExp))).withContextPos();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
package kiss;
|
||||
|
||||
import haxe.macro.Expr;
|
||||
import kiss.Reader;
|
||||
|
||||
// A lot of special forms need to convert expressions recursively, so they get passed an ExprConversion
|
||||
typedef ExprConversion = (ReaderExp) -> Expr;
|
||||
Reference in New Issue
Block a user