refactor ExpBuilder to be a class, not anonymous object
This commit is contained in:
@@ -5,137 +5,164 @@ using kiss.Helpers;
|
||||
using kiss.Reader;
|
||||
using kiss.ExpBuilder;
|
||||
|
||||
// Has convenient functions for succinctly making new ReaderExps that link back to an original exp's
|
||||
// position in source code
|
||||
class ExpBuilder {
|
||||
// Return convenient functions for succinctly making new ReaderExps that link back to an original exp's
|
||||
// position in source code
|
||||
var posRef:ReaderExp;
|
||||
|
||||
function new(posRef:ReaderExp) {
|
||||
this.posRef = posRef;
|
||||
}
|
||||
|
||||
public static function expBuilder(posRef:ReaderExp) {
|
||||
function _symbol(?name:String) {
|
||||
return Prelude.symbol(name).withPosOf(posRef);
|
||||
}
|
||||
function call(func:ReaderExp, args:Array<ReaderExp>) {
|
||||
return CallExp(func, args).withPosOf(posRef);
|
||||
}
|
||||
function callSymbol(symbol:String, args:Array<ReaderExp>) {
|
||||
return call(_symbol(symbol), args);
|
||||
}
|
||||
function field(f:String, exp:ReaderExp, ?safe:Bool) {
|
||||
return FieldExp(f, exp, safe != null && safe).withPosOf(posRef);
|
||||
}
|
||||
function list(exps:Array<ReaderExp>) {
|
||||
return ListExp(exps).withPosOf(posRef);
|
||||
}
|
||||
function objectWith (bindings:Array<ReaderExp>, captures:Array<ReaderExp>) {
|
||||
return callSymbol("objectWith", [list(bindings)].concat(captures));
|
||||
}
|
||||
function str(s:String) {
|
||||
return StrExp(s).withPosOf(posRef);
|
||||
}
|
||||
function raw(code:String) {
|
||||
return RawHaxe(code).withPosOf(posRef);
|
||||
}
|
||||
function int(v:Int) {
|
||||
return _symbol(Std.string(v));
|
||||
}
|
||||
function float(v:Float) {
|
||||
return _symbol(Std.string(v));
|
||||
}
|
||||
function let(bindings:Array<ReaderExp>, body:Array<ReaderExp>) {
|
||||
return callSymbol("let", [list(bindings)].concat(body));
|
||||
}
|
||||
function _if(condition:ReaderExp, then:ReaderExp, ?_else:ReaderExp) {
|
||||
var args = [condition, then];
|
||||
if (_else != null)
|
||||
args.push(_else);
|
||||
return callSymbol("if", args);
|
||||
}
|
||||
return new ExpBuilder(posRef);
|
||||
}
|
||||
|
||||
public function symbol(?name:String) {
|
||||
return Prelude.symbol(name).withPosOf(posRef);
|
||||
}
|
||||
|
||||
public function call(func:ReaderExp, args:Array<ReaderExp>) {
|
||||
return CallExp(func, args).withPosOf(posRef);
|
||||
}
|
||||
|
||||
public function callSymbol(_symbol:String, args:Array<ReaderExp>) {
|
||||
return call(symbol(_symbol), args);
|
||||
}
|
||||
|
||||
public function field(f:String, exp:ReaderExp, ?safe:Bool) {
|
||||
return FieldExp(f, exp, safe != null && safe).withPosOf(posRef);
|
||||
}
|
||||
|
||||
public function list(exps:Array<ReaderExp>) {
|
||||
return ListExp(exps).withPosOf(posRef);
|
||||
}
|
||||
public function objectWith (bindings:Array<ReaderExp>, captures:Array<ReaderExp>) {
|
||||
return callSymbol("objectWith", [list(bindings)].concat(captures));
|
||||
}
|
||||
public function str(s:String) {
|
||||
return StrExp(s).withPosOf(posRef);
|
||||
}
|
||||
public function raw(code:String) {
|
||||
return RawHaxe(code).withPosOf(posRef);
|
||||
}
|
||||
public function int(v:Int) {
|
||||
return symbol(Std.string(v));
|
||||
}
|
||||
public function float(v:Float) {
|
||||
return symbol(Std.string(v));
|
||||
}
|
||||
public function let(bindings:Array<ReaderExp>, body:Array<ReaderExp>) {
|
||||
return callSymbol("let", [list(bindings)].concat(body));
|
||||
}
|
||||
public function _if(condition:ReaderExp, then:ReaderExp, ?_else:ReaderExp) {
|
||||
var args = [condition, then];
|
||||
if (_else != null)
|
||||
args.push(_else);
|
||||
return callSymbol("if", args);
|
||||
}
|
||||
#if (sys || hxnodejs)
|
||||
function throwAssertOrNeverError(messageExp:ReaderExp) {
|
||||
var failureError = KissError.fromExp(posRef, "").toString(AssertionFail);
|
||||
var colonsInPrefix = if (Sys.systemName() == "Windows") 5 else 4;
|
||||
return callSymbol("throw", [
|
||||
callSymbol("kiss.Prelude.runtimeInsertAssertionMessage", [messageExp, str(failureError), int(colonsInPrefix)])
|
||||
]);
|
||||
}
|
||||
public function throwAssertOrNeverError(messageExp:ReaderExp) {
|
||||
var failureError = KissError.fromExp(posRef, "").toString(AssertionFail);
|
||||
var colonsInPrefix = if (Sys.systemName() == "Windows") 5 else 4;
|
||||
return callSymbol("throw", [
|
||||
callSymbol("kiss.Prelude.runtimeInsertAssertionMessage", [messageExp, str(failureError), int(colonsInPrefix)])
|
||||
]);
|
||||
}
|
||||
#end
|
||||
function whenUnless(which:String, condition:ReaderExp, body:Array<ReaderExp>) {
|
||||
return callSymbol(which, [condition].concat(body));
|
||||
}
|
||||
return {
|
||||
call: call,
|
||||
callSymbol: callSymbol,
|
||||
callField: (fieldName:String, callOn:ReaderExp, args:Array<ReaderExp>) -> call(field(fieldName, callOn), args),
|
||||
print: (arg:ReaderExp) -> CallExp(Symbol("print").withPosOf(posRef), [arg]).withPosOf(posRef),
|
||||
the: (type:ReaderExp, value:ReaderExp) -> callSymbol("the", [type, value]),
|
||||
not: (exp:ReaderExp) -> callSymbol("not", [exp]),
|
||||
list: list,
|
||||
str: str,
|
||||
symbol: _symbol,
|
||||
_if: _if,
|
||||
int: int,
|
||||
float: float,
|
||||
raw: raw,
|
||||
typed: (path:String, exp:ReaderExp) -> TypedExp(path, exp).withPosOf(posRef),
|
||||
meta: (m:String, exp:ReaderExp) -> MetaExp(m, exp).withPosOf(posRef),
|
||||
field: field,
|
||||
keyValue: (key:ReaderExp, value:ReaderExp) -> KeyValueExp(key, value).withPosOf(posRef),
|
||||
begin: (exps:Array<ReaderExp>) -> callSymbol("begin", exps),
|
||||
set: (v:ReaderExp, value:ReaderExp) -> callSymbol("set", [v, value]),
|
||||
when: whenUnless.bind("when"),
|
||||
unless: whenUnless.bind("unless"),
|
||||
let: let,
|
||||
objectWith: objectWith,
|
||||
expFromDef: (def:ReaderExpDef) -> def.withPosOf(posRef),
|
||||
function _whenUnless(which:String, condition:ReaderExp, body:Array<ReaderExp>) {
|
||||
return callSymbol(which, [condition].concat(body));
|
||||
}
|
||||
public function when(condition:ReaderExp, body:Array<ReaderExp>) {
|
||||
return _whenUnless("when", condition, body);
|
||||
}
|
||||
public function unless(condition:ReaderExp, body:Array<ReaderExp>) {
|
||||
return _whenUnless("unless", condition, body);
|
||||
}
|
||||
|
||||
public function callField(fieldName:String, callOn:ReaderExp, args:Array<ReaderExp>) {
|
||||
return call(field(fieldName, callOn), args);
|
||||
}
|
||||
public function print(arg:ReaderExp) {
|
||||
return CallExp(Symbol("print").withPosOf(posRef), [arg]).withPosOf(posRef);
|
||||
}
|
||||
public function the(type:ReaderExp, value:ReaderExp) {
|
||||
return callSymbol("the", [type, value]);
|
||||
}
|
||||
public function not(exp:ReaderExp) {
|
||||
return callSymbol("not", [exp]);
|
||||
}
|
||||
public function typed(path:String, exp:ReaderExp) {
|
||||
return TypedExp(path, exp).withPosOf(posRef);
|
||||
}
|
||||
public function meta(m:String, exp:ReaderExp) {
|
||||
return MetaExp(m, exp).withPosOf(posRef);
|
||||
}
|
||||
public function keyValue(key:ReaderExp, value:ReaderExp) {
|
||||
return KeyValueExp(key, value).withPosOf(posRef);
|
||||
}
|
||||
public function begin(exps:Array<ReaderExp>) {
|
||||
return callSymbol("begin", exps);
|
||||
}
|
||||
public function set(v:ReaderExp, value:ReaderExp) {
|
||||
return callSymbol("set", [v, value]);
|
||||
}
|
||||
public function expFromDef(def:ReaderExpDef) {
|
||||
return def.withPosOf(posRef);
|
||||
}
|
||||
#if (sys || hxnodejs)
|
||||
// Only use within assertion macros
|
||||
throwAssertionError: () -> {
|
||||
var usage = "throwAssertionError can only be used in a builder of an assertion macro";
|
||||
var exps = switch (posRef.def) {
|
||||
case CallExp(_, exps):
|
||||
exps;
|
||||
default:
|
||||
throw KissError.fromExp(_symbol("throwAssertionError"), usage);
|
||||
}
|
||||
var messageExp = if (exps.length > 1) {
|
||||
exps[1];
|
||||
} else {
|
||||
str("");
|
||||
};
|
||||
throwAssertOrNeverError(messageExp);
|
||||
},
|
||||
neverCase: () -> {
|
||||
switch (posRef.def) {
|
||||
case CallExp({pos: _, def: Symbol("never")}, neverExps):
|
||||
posRef.checkNumArgs(1, 1, '(never <pattern>)');
|
||||
call(neverExps[0], [
|
||||
throwAssertOrNeverError(str('case should never match pattern ${Reader.toString(neverExps[0].def)}'))
|
||||
]);
|
||||
default:
|
||||
posRef;
|
||||
}
|
||||
},
|
||||
#end
|
||||
// Compile-time only!
|
||||
throwKissError: (reason:String) -> {
|
||||
callSymbol("throw", [
|
||||
callSymbol("KissError.fromExpStr", [
|
||||
// pos
|
||||
objectWith([
|
||||
_symbol("file"), str(posRef.pos.file),
|
||||
_symbol("line"), int(posRef.pos.line),
|
||||
_symbol("column"), int(posRef.pos.column),
|
||||
_symbol("absoluteChar"), int(posRef.pos.absoluteChar),
|
||||
], []),
|
||||
// expStr
|
||||
str(Reader.toString(posRef.def)),
|
||||
str(reason)
|
||||
])
|
||||
]);
|
||||
},
|
||||
#if macro
|
||||
haxeExpr: (e:haxe.macro.Expr) -> Helpers.withMacroPosOf(e.expr, posRef),
|
||||
#end
|
||||
none: () -> None.withPosOf(posRef)
|
||||
// Only use within assertion macros
|
||||
public function throwAssertionError() {
|
||||
var usage = "throwAssertionError can only be used in a builder of an assertion macro";
|
||||
var exps = switch (posRef.def) {
|
||||
case CallExp(_, exps):
|
||||
exps;
|
||||
default:
|
||||
throw KissError.fromExp(symbol("throwAssertionError"), usage);
|
||||
}
|
||||
var messageExp = if (exps.length > 1) {
|
||||
exps[1];
|
||||
} else {
|
||||
str("");
|
||||
};
|
||||
return throwAssertOrNeverError(messageExp);
|
||||
}
|
||||
public function neverCase() {
|
||||
return switch (posRef.def) {
|
||||
case CallExp({pos: _, def: Symbol("never")}, neverExps):
|
||||
posRef.checkNumArgs(1, 1, '(never <pattern>)');
|
||||
call(neverExps[0], [
|
||||
throwAssertOrNeverError(str('case should never match pattern ${Reader.toString(neverExps[0].def)}'))
|
||||
]);
|
||||
default:
|
||||
posRef;
|
||||
}
|
||||
}
|
||||
#end
|
||||
// Compile-time only!
|
||||
public function throwKissError(reason:String) {
|
||||
return callSymbol("throw", [
|
||||
callSymbol("KissError.fromExpStr", [
|
||||
// pos
|
||||
objectWith([
|
||||
symbol("file"), str(posRef.pos.file),
|
||||
symbol("line"), int(posRef.pos.line),
|
||||
symbol("column"), int(posRef.pos.column),
|
||||
symbol("absoluteChar"), int(posRef.pos.absoluteChar),
|
||||
], []),
|
||||
// expStr
|
||||
str(Reader.toString(posRef.def)),
|
||||
str(reason)
|
||||
])
|
||||
]);
|
||||
}
|
||||
#if macro
|
||||
public function haxeExpr(e:haxe.macro.Expr) {
|
||||
return Helpers.withMacroPosOf(e.expr, posRef);
|
||||
}
|
||||
#end
|
||||
public function none() {
|
||||
return None.withPosOf(posRef);
|
||||
}
|
||||
|
||||
public static function checkNumArgs(wholeExp:ReaderExp, min:Null<Int>, max:Null<Int>, ?expectedForm:String) {
|
||||
|
Reference in New Issue
Block a user