Lisp macro back-end w/ +, - as examples
This commit is contained in:
@@ -71,7 +71,7 @@ class FieldForms {
|
|||||||
access: access,
|
access: access,
|
||||||
kind: FFun({
|
kind: FFun({
|
||||||
args: switch (args[1]) {
|
args: switch (args[1]) {
|
||||||
case List(funcArgs):
|
case ListExp(funcArgs):
|
||||||
[
|
[
|
||||||
for (funcArg in funcArgs)
|
for (funcArg in funcArgs)
|
||||||
{
|
{
|
||||||
@@ -90,7 +90,7 @@ class FieldForms {
|
|||||||
ret: null,
|
ret: null,
|
||||||
expr: {
|
expr: {
|
||||||
pos: Context.currentPos(),
|
pos: Context.currentPos(),
|
||||||
expr: EReturn(convert(Call(Symbol("begin"), args.slice(2))))
|
expr: EReturn(convert(CallExp(Symbol("begin"), args.slice(2))))
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
pos: Context.currentPos()
|
pos: Context.currentPos()
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import kiss.Stream;
|
|||||||
import kiss.Reader;
|
import kiss.Reader;
|
||||||
import kiss.FieldForms;
|
import kiss.FieldForms;
|
||||||
import kiss.SpecialForms;
|
import kiss.SpecialForms;
|
||||||
|
import kiss.Macros;
|
||||||
|
|
||||||
class Kiss {
|
class Kiss {
|
||||||
/**
|
/**
|
||||||
@@ -19,6 +20,7 @@ class Kiss {
|
|||||||
var readTable = Reader.builtins();
|
var readTable = Reader.builtins();
|
||||||
var fieldForms = FieldForms.builtins();
|
var fieldForms = FieldForms.builtins();
|
||||||
var specialForms = SpecialForms.builtins();
|
var specialForms = SpecialForms.builtins();
|
||||||
|
var macros = Macros.builtins();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
stream.dropWhitespace();
|
stream.dropWhitespace();
|
||||||
@@ -32,7 +34,7 @@ class Kiss {
|
|||||||
// The last expression might be a comment, in which case None will be returned
|
// The last expression might be a comment, in which case None will be returned
|
||||||
switch (nextExp) {
|
switch (nextExp) {
|
||||||
case Some(nextExp):
|
case Some(nextExp):
|
||||||
classFields.push(readerExpToField(nextExp, position, fieldForms, specialForms));
|
classFields.push(readerExpToField(nextExp, position, fieldForms, macros, specialForms));
|
||||||
case None:
|
case None:
|
||||||
stream.dropWhitespace(); // If there was a comment, drop whitespace that comes after
|
stream.dropWhitespace(); // If there was a comment, drop whitespace that comes after
|
||||||
}
|
}
|
||||||
@@ -41,33 +43,37 @@ class Kiss {
|
|||||||
return classFields;
|
return classFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function readerExpToField(exp:ReaderExp, position:String, fieldForms:Map<String, FieldFormFunction>,
|
static function readerExpToField(exp:ReaderExp, position:String, fieldForms:Map<String, FieldFormFunction>, macros:Map<String, MacroFunction>,
|
||||||
specialForms:Map<String, SpecialFormFunction>):Field {
|
specialForms:Map<String, SpecialFormFunction>):Field {
|
||||||
return switch (exp) {
|
return switch (exp) {
|
||||||
case Call(Symbol(formName), args) if (fieldForms.exists(formName)):
|
case CallExp(Symbol(formName), args) if (fieldForms.exists(formName)):
|
||||||
fieldForms[formName](position, args, readerExpToHaxeExpr.bind(_, specialForms));
|
fieldForms[formName](position, args, readerExpToHaxeExpr.bind(_, macros, specialForms));
|
||||||
default:
|
default:
|
||||||
throw '$exp at $position is not a valid field form';
|
throw '$exp at $position is not a valid field form';
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static function readerExpToHaxeExpr(exp:ReaderExp, specialForms:Map<String, SpecialFormFunction>):Expr {
|
static function readerExpToHaxeExpr(exp:ReaderExp, macros:Map<String, MacroFunction>, specialForms:Map<String, SpecialFormFunction>):Expr {
|
||||||
|
// Bind the table arguments of this function for easy recursive calling/passing
|
||||||
|
var convert = readerExpToHaxeExpr.bind(_, macros, specialForms);
|
||||||
var expr = switch (exp) {
|
var expr = switch (exp) {
|
||||||
case Symbol(name):
|
case Symbol(name):
|
||||||
Context.parse(name, Context.currentPos());
|
Context.parse(name, Context.currentPos());
|
||||||
case Str(s):
|
case StrExp(s):
|
||||||
{
|
{
|
||||||
pos: Context.currentPos(),
|
pos: Context.currentPos(),
|
||||||
expr: EConst(CString(s))
|
expr: EConst(CString(s))
|
||||||
};
|
};
|
||||||
case Call(Symbol(specialForm), args) if (specialForms.exists(specialForm)):
|
case CallExp(Symbol(mac), args) if (macros.exists(mac)):
|
||||||
specialForms[specialForm](args, readerExpToHaxeExpr.bind(_, specialForms));
|
convert(macros[mac](args));
|
||||||
case Call(func, body):
|
case CallExp(Symbol(specialForm), args) if (specialForms.exists(specialForm)):
|
||||||
|
specialForms[specialForm](args, convert);
|
||||||
|
case CallExp(func, body):
|
||||||
{
|
{
|
||||||
pos: Context.currentPos(),
|
pos: Context.currentPos(),
|
||||||
expr: ECall(readerExpToHaxeExpr(func, specialForms), [for (bodyExp in body) readerExpToHaxeExpr(bodyExp, specialForms)])
|
expr: ECall(readerExpToHaxeExpr(func, macros, specialForms), [for (bodyExp in body) readerExpToHaxeExpr(bodyExp, macros, specialForms)])
|
||||||
};
|
};
|
||||||
case List(elements):
|
case ListExp(elements):
|
||||||
{
|
{
|
||||||
pos: Context.currentPos(),
|
pos: Context.currentPos(),
|
||||||
expr: ENew({
|
expr: ENew({
|
||||||
@@ -76,7 +82,7 @@ class Kiss {
|
|||||||
}, [
|
}, [
|
||||||
{
|
{
|
||||||
pos: Context.currentPos(),
|
pos: Context.currentPos(),
|
||||||
expr: EArrayDecl([for (elementExp in elements) readerExpToHaxeExpr(elementExp, specialForms)])
|
expr: EArrayDecl([for (elementExp in elements) convert(elementExp)])
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|||||||
22
src/kiss/Macros.hx
Normal file
22
src/kiss/Macros.hx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package kiss;
|
||||||
|
|
||||||
|
import kiss.Reader;
|
||||||
|
|
||||||
|
// Macros generate Kiss new reader from the arguments of their call expression.
|
||||||
|
typedef MacroFunction = (Array<ReaderExp>) -> ReaderExp;
|
||||||
|
|
||||||
|
class Macros {
|
||||||
|
public static function builtins() {
|
||||||
|
var macros:Map<String, MacroFunction> = [];
|
||||||
|
|
||||||
|
macros["+"] = (exps) -> {
|
||||||
|
CallExp(Symbol("Lambda.fold"), [ListExp(exps), Symbol("Prelude.add"), Symbol("0")]);
|
||||||
|
};
|
||||||
|
|
||||||
|
macros["-"] = (exps:Array<ReaderExp>) -> {
|
||||||
|
CallExp(Symbol("Lambda.fold"), [ListExp(exps.slice(1)), Symbol("Prelude.subtract"), exps[0]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return macros;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/kiss/Prelude.hx
Normal file
11
src/kiss/Prelude.hx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package kiss;
|
||||||
|
|
||||||
|
class Prelude {
|
||||||
|
public static function add(a, b) {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function subtract(val, from) {
|
||||||
|
return from - val;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,9 +4,9 @@ import haxe.ds.Option;
|
|||||||
import kiss.Stream;
|
import kiss.Stream;
|
||||||
|
|
||||||
enum ReaderExp {
|
enum ReaderExp {
|
||||||
Call(func:ReaderExp, args:Array<ReaderExp>); // (f a1 a2...)
|
CallExp(func:ReaderExp, args:Array<ReaderExp>); // (f a1 a2...)
|
||||||
List(exps:Array<ReaderExp>); // [v1 v2 v3]
|
ListExp(exps:Array<ReaderExp>); // [v1 v2 v3]
|
||||||
Str(s:String); // "literal"
|
StrExp(s:String); // "literal"
|
||||||
Symbol(name:String); // s
|
Symbol(name:String); // s
|
||||||
RawHaxe(code:String);
|
RawHaxe(code:String);
|
||||||
}
|
}
|
||||||
@@ -18,9 +18,9 @@ class Reader {
|
|||||||
public static function builtins() {
|
public static function builtins() {
|
||||||
var readTable:Map<String, ReadFunction> = [];
|
var readTable:Map<String, ReadFunction> = [];
|
||||||
|
|
||||||
readTable["("] = (stream) -> Call(assertRead(stream, readTable), readExpArray(stream, ")", readTable));
|
readTable["("] = (stream) -> CallExp(assertRead(stream, readTable), readExpArray(stream, ")", readTable));
|
||||||
readTable["["] = (stream) -> List(readExpArray(stream, "]", readTable));
|
readTable["["] = (stream) -> ListExp(readExpArray(stream, "]", readTable));
|
||||||
readTable["\""] = (stream) -> Str(stream.expect("closing \"", () -> stream.takeUntilAndDrop("\"")));
|
readTable["\""] = (stream) -> StrExp(stream.expect("closing \"", () -> stream.takeUntilAndDrop("\"")));
|
||||||
readTable["/*"] = (stream) -> {
|
readTable["/*"] = (stream) -> {
|
||||||
stream.dropUntil("*/");
|
stream.dropUntil("*/");
|
||||||
stream.dropString("*/");
|
stream.dropString("*/");
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package test.cases;
|
|||||||
|
|
||||||
import utest.Test;
|
import utest.Test;
|
||||||
import utest.Assert;
|
import utest.Assert;
|
||||||
|
import kiss.Prelude;
|
||||||
|
|
||||||
@:build(kiss.Kiss.build("src/test/cases/BasicTestCase.kiss"))
|
@:build(kiss.Kiss.build("src/test/cases/BasicTestCase.kiss"))
|
||||||
class BasicTestCase extends Test {
|
class BasicTestCase extends Test {
|
||||||
@@ -41,4 +42,12 @@ class BasicTestCase extends Test {
|
|||||||
Assert.equals(2, arr[-2]);
|
Assert.equals(2, arr[-2]);
|
||||||
Assert.equals(1, arr[-3]);
|
Assert.equals(1, arr[-3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testVariadicAdd() {
|
||||||
|
Assert.equals(6, BasicTestCase.mySum);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testVariadicSubtract() {
|
||||||
|
Assert.equals(-2, BasicTestCase.myDifference);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,3 +20,8 @@
|
|||||||
|
|
||||||
// [...] returns a Kiss array (they have special features and convert implicitly)
|
// [...] returns a Kiss array (they have special features and convert implicitly)
|
||||||
(defvar myArray [1 2 3])
|
(defvar myArray [1 2 3])
|
||||||
|
|
||||||
|
// Variadic math uses haxe's Lambda.fold under the hood
|
||||||
|
(defvar mySum (+ 1 2 3))
|
||||||
|
|
||||||
|
(defvar myDifference (- 5 4 3))
|
||||||
Reference in New Issue
Block a user