refactor ExpBuilder to be a class, not anonymous object

This commit is contained in:
2025-02-13 09:15:41 -06:00
parent fb34feb610
commit bb18997606

View File

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