Pass KissState instead of convert

This commit is contained in:
2020-11-30 13:23:01 -07:00
parent 52e310d01a
commit 3f5e81f03e
5 changed files with 61 additions and 66 deletions

View File

@@ -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()
};
}

View File

@@ -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()
}
}

View File

@@ -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):

View File

@@ -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();
};
}
}

View File

@@ -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;