refactor: FieldForms.hx
This commit is contained in:
102
src/kiss/FieldForms.hx
Normal file
102
src/kiss/FieldForms.hx
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
package kiss;
|
||||||
|
|
||||||
|
import haxe.macro.Expr;
|
||||||
|
import haxe.macro.Context;
|
||||||
|
import kiss.Reader;
|
||||||
|
|
||||||
|
using StringTools;
|
||||||
|
|
||||||
|
// Fields usually are set to expressions, so a ReaderExp conversion function
|
||||||
|
// needs to be passed to field forms
|
||||||
|
typedef ExprConversion = (ReaderExp) -> Expr;
|
||||||
|
|
||||||
|
// Field forms convert Kiss reader expressions into Haxe macro class fields
|
||||||
|
typedef FieldFormFunction = (position:String, args:Array<ReaderExp>, convert:ExprConversion) -> Field;
|
||||||
|
|
||||||
|
class FieldForms {
|
||||||
|
public static function builtins() {
|
||||||
|
var map:Map<String, FieldFormFunction> = [];
|
||||||
|
|
||||||
|
map["defvar"] = varOrProperty.bind("defvar");
|
||||||
|
map["defprop"] = varOrProperty.bind("defprop");
|
||||||
|
|
||||||
|
map["defun"] = funcOrMethod.bind("defun");
|
||||||
|
map["defmethod"] = funcOrMethod.bind("defmethod");
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function fieldAccess(formName:String, fieldName:String) {
|
||||||
|
var access = [];
|
||||||
|
if (formName == "defvar" || formName == "defun") {
|
||||||
|
access.push(AStatic);
|
||||||
|
}
|
||||||
|
access.push(if (fieldName.startsWith("_")) APrivate else APublic);
|
||||||
|
return access;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function fieldName(formName:String, position:String, nameExp:ReaderExp) {
|
||||||
|
return switch (nameExp) {
|
||||||
|
case Symbol(name):
|
||||||
|
name;
|
||||||
|
default:
|
||||||
|
throw 'The first argument to $formName at $position should be a variable name';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static function varOrProperty(formName:String, position:String, args:Array<ReaderExp>, convert:ExprConversion):Field {
|
||||||
|
if (args.length != 2) {
|
||||||
|
throw '$formName with $args at $position is not a valid field definition';
|
||||||
|
}
|
||||||
|
|
||||||
|
var name = fieldName(formName, position, args[0]);
|
||||||
|
var access = fieldAccess(formName, name);
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: name,
|
||||||
|
access: access,
|
||||||
|
kind: FVar(null, // TODO allow type anotations
|
||||||
|
convert(args[1])),
|
||||||
|
pos: Context.currentPos()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static function funcOrMethod(formName:String, position:String, args:Array<ReaderExp>, convert:ExprConversion):Field {
|
||||||
|
if (args.length <= 2) {
|
||||||
|
throw '$formName with $args is not a valid function/method definition';
|
||||||
|
}
|
||||||
|
|
||||||
|
var name = fieldName(formName, position, args[0]);
|
||||||
|
var access = fieldAccess(formName, name);
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: name,
|
||||||
|
access: access,
|
||||||
|
kind: FFun({
|
||||||
|
args: switch (args[1]) {
|
||||||
|
case List(funcArgs):
|
||||||
|
[
|
||||||
|
for (funcArg in funcArgs)
|
||||||
|
{
|
||||||
|
name: switch (funcArg) {
|
||||||
|
case Symbol(name):
|
||||||
|
name;
|
||||||
|
default:
|
||||||
|
throw '$funcArg should be a symbol for a function argument';
|
||||||
|
},
|
||||||
|
type: null
|
||||||
|
}
|
||||||
|
];
|
||||||
|
default:
|
||||||
|
throw '$args[1] should be an argument list';
|
||||||
|
},
|
||||||
|
ret: null,
|
||||||
|
expr: {
|
||||||
|
pos: Context.currentPos(),
|
||||||
|
expr: EReturn(convert(Call(Symbol("begin"), args.slice(2))))
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
pos: Context.currentPos()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import haxe.macro.Context;
|
|||||||
import haxe.macro.Expr;
|
import haxe.macro.Expr;
|
||||||
import kiss.Stream;
|
import kiss.Stream;
|
||||||
import kiss.Reader;
|
import kiss.Reader;
|
||||||
|
import kiss.FieldForms;
|
||||||
|
|
||||||
class Kiss {
|
class Kiss {
|
||||||
/**
|
/**
|
||||||
@@ -14,6 +15,9 @@ class Kiss {
|
|||||||
|
|
||||||
var stream = new Stream(kissFile);
|
var stream = new Stream(kissFile);
|
||||||
var reader = new Reader();
|
var reader = new Reader();
|
||||||
|
|
||||||
|
var fieldForms = FieldForms.builtins();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
stream.dropWhitespace();
|
stream.dropWhitespace();
|
||||||
if (stream.isEmpty())
|
if (stream.isEmpty())
|
||||||
@@ -26,7 +30,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));
|
classFields.push(readerExpToField(nextExp, position, fieldForms));
|
||||||
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
|
||||||
}
|
}
|
||||||
@@ -35,58 +39,12 @@ class Kiss {
|
|||||||
return classFields;
|
return classFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function readerExpToField(exp:ReaderExp, position:String):Field {
|
static function readerExpToField(exp:ReaderExp, position:String, fieldForms:Map<String, FieldFormFunction>):Field {
|
||||||
return switch (exp) {
|
return switch (exp) {
|
||||||
case Call(Symbol("defvar"), args) if (args.length == 2):
|
case Call(Symbol(formName), args) if (fieldForms.exists(formName)):
|
||||||
{
|
fieldForms[formName](position, args, readerExpToHaxeExpr);
|
||||||
name: switch (args[0]) {
|
|
||||||
case Symbol(name):
|
|
||||||
name;
|
|
||||||
default:
|
|
||||||
throw 'The first argument to defvar at $position should be a variable name';
|
|
||||||
},
|
|
||||||
access: [APublic, AStatic],
|
|
||||||
kind: FVar(null, // TODO allow type anotations
|
|
||||||
readerExpToHaxeExpr(args[1])),
|
|
||||||
pos: Context.currentPos()
|
|
||||||
};
|
|
||||||
case Call(Symbol("defun"), args) if (args.length > 2):
|
|
||||||
{
|
|
||||||
name: switch (args[0]) {
|
|
||||||
case Symbol(name):
|
|
||||||
name;
|
|
||||||
default:
|
|
||||||
throw 'The first argument to defun at $position should be a function name';
|
|
||||||
},
|
|
||||||
access: [APublic, AStatic],
|
|
||||||
kind: FFun({
|
|
||||||
args: switch (args[1]) {
|
|
||||||
case List(funcArgs):
|
|
||||||
[
|
|
||||||
for (funcArg in funcArgs)
|
|
||||||
{
|
|
||||||
name: switch (funcArg) {
|
|
||||||
case Symbol(name):
|
|
||||||
name;
|
|
||||||
default:
|
|
||||||
throw '$funcArg should be a symbol for a function argument';
|
|
||||||
},
|
|
||||||
type: null
|
|
||||||
}
|
|
||||||
];
|
|
||||||
default:
|
|
||||||
throw '$args[1] should be an argument list';
|
|
||||||
},
|
|
||||||
ret: null,
|
|
||||||
expr: {
|
|
||||||
pos: Context.currentPos(),
|
|
||||||
expr: EReturn(readerExpToHaxeExpr(Call(Symbol("begin"), args.slice(2))))
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
pos: Context.currentPos()
|
|
||||||
};
|
|
||||||
default:
|
default:
|
||||||
throw '$exp at $position is not a valid defvar or defun expression';
|
throw '$exp at $position is not a valid field form';
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,4 +20,12 @@ class BasicTestCase extends Test {
|
|||||||
function testFuncall() {
|
function testFuncall() {
|
||||||
Assert.equals(7, BasicTestCase.funResult);
|
Assert.equals(7, BasicTestCase.funResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testField() {
|
||||||
|
Assert.equals(5, new BasicTestCase().myField);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testMethod() {
|
||||||
|
Assert.equals(5, new BasicTestCase().myMethod());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,4 +7,10 @@
|
|||||||
// funcalls can use dot access
|
// funcalls can use dot access
|
||||||
(Math.floor num))
|
(Math.floor num))
|
||||||
// functions are resolved in the macro context
|
// functions are resolved in the macro context
|
||||||
(defvar funResult (myFloor 7.5))
|
(defvar funResult (myFloor 7.5))
|
||||||
|
|
||||||
|
// (defprop) declares instance variables
|
||||||
|
(defprop myField 5)
|
||||||
|
|
||||||
|
// (defmethod) declares instance methods
|
||||||
|
(defmethod myMethod [] this.myField)
|
||||||
Reference in New Issue
Block a user