Allow type hints on functions and vars
This commit is contained in:
@@ -4,6 +4,7 @@ import haxe.macro.Expr;
|
|||||||
import haxe.macro.Context;
|
import haxe.macro.Context;
|
||||||
import kiss.Reader;
|
import kiss.Reader;
|
||||||
import kiss.Types;
|
import kiss.Types;
|
||||||
|
import kiss.Helpers;
|
||||||
|
|
||||||
using StringTools;
|
using StringTools;
|
||||||
|
|
||||||
@@ -34,10 +35,10 @@ class FieldForms {
|
|||||||
|
|
||||||
static function fieldName(formName:String, position:String, nameExp:ReaderExp) {
|
static function fieldName(formName:String, position:String, nameExp:ReaderExp) {
|
||||||
return switch (nameExp) {
|
return switch (nameExp) {
|
||||||
case Symbol(name):
|
case Symbol(name) | TypedExp(_, Symbol(name)):
|
||||||
name;
|
name;
|
||||||
default:
|
default:
|
||||||
throw 'The first argument to $formName at $position should be a variable name';
|
throw 'The first argument to $formName at $position should be a variable name or typed variable name';
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,8 +53,11 @@ class FieldForms {
|
|||||||
return {
|
return {
|
||||||
name: name,
|
name: name,
|
||||||
access: access,
|
access: access,
|
||||||
kind: FVar(null, // TODO allow type anotations
|
kind: FVar(switch (args[0]) {
|
||||||
convert(args[1])),
|
case TypedExp(type, _):
|
||||||
|
Helpers.parseTypePath(type);
|
||||||
|
default: null;
|
||||||
|
}, convert(args[1])),
|
||||||
pos: Context.currentPos()
|
pos: Context.currentPos()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -70,10 +74,12 @@ class FieldForms {
|
|||||||
return {
|
return {
|
||||||
name: name,
|
name: name,
|
||||||
access: access,
|
access: access,
|
||||||
|
// TODO type parameter declarations
|
||||||
kind: FFun({
|
kind: FFun({
|
||||||
args: switch (args[1]) {
|
args: switch (args[1]) {
|
||||||
case ListExp(funcArgs):
|
case ListExp(funcArgs):
|
||||||
[
|
[
|
||||||
|
// TODO optional arguments, default values
|
||||||
for (funcArg in funcArgs)
|
for (funcArg in funcArgs)
|
||||||
{
|
{
|
||||||
name: switch (funcArg) {
|
name: switch (funcArg) {
|
||||||
@@ -82,7 +88,11 @@ class FieldForms {
|
|||||||
default:
|
default:
|
||||||
throw '$funcArg should be a symbol or typed symbol for a function argument';
|
throw '$funcArg should be a symbol or typed symbol for a function argument';
|
||||||
},
|
},
|
||||||
type: null
|
type: switch (funcArg) {
|
||||||
|
case TypedExp(type, _):
|
||||||
|
Helpers.parseTypePath(type);
|
||||||
|
default: null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
case CallExp(_, _):
|
case CallExp(_, _):
|
||||||
@@ -90,7 +100,10 @@ class FieldForms {
|
|||||||
default:
|
default:
|
||||||
throw '${args[1]} should be an argument list';
|
throw '${args[1]} should be an argument list';
|
||||||
},
|
},
|
||||||
ret: null,
|
ret: switch (args[0]) {
|
||||||
|
case TypedExp(type, _): Helpers.parseTypePath(type);
|
||||||
|
default: null;
|
||||||
|
},
|
||||||
expr: {
|
expr: {
|
||||||
pos: Context.currentPos(),
|
pos: Context.currentPos(),
|
||||||
expr: EReturn(convert(CallExp(Symbol("begin"), args.slice(2))))
|
expr: EReturn(convert(CallExp(Symbol("begin"), args.slice(2))))
|
||||||
|
@@ -3,6 +3,8 @@ package kiss;
|
|||||||
import haxe.macro.Expr;
|
import haxe.macro.Expr;
|
||||||
import haxe.macro.Context;
|
import haxe.macro.Context;
|
||||||
|
|
||||||
|
using StringTools;
|
||||||
|
|
||||||
class Helpers {
|
class Helpers {
|
||||||
public static function withPos(e:ExprDef):Expr {
|
public static function withPos(e:ExprDef):Expr {
|
||||||
return {
|
return {
|
||||||
@@ -10,4 +12,35 @@ class Helpers {
|
|||||||
expr: e
|
expr: e
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static function startsWithUpperCase(s:String) {
|
||||||
|
return s.charAt(0) == s.charAt(0).toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO this doesn't parse generic typeparams yet
|
||||||
|
public static function parseTypePath(path:String):ComplexType {
|
||||||
|
var parts:List<String> = path.split(".");
|
||||||
|
var uppercaseParts:List<Bool> = parts.map(startsWithUpperCase);
|
||||||
|
for (isUpcase in uppercaseParts.slice(0, -2)) {
|
||||||
|
if (isUpcase) {
|
||||||
|
throw 'Type path $path should only have capitalized type and subtype';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var lastIsCap = uppercaseParts[-1];
|
||||||
|
var penultIsCap = uppercaseParts[-2];
|
||||||
|
return TPath(if (lastIsCap && penultIsCap) {
|
||||||
|
{
|
||||||
|
sub: parts[-1],
|
||||||
|
name: parts[-2],
|
||||||
|
pack: parts.slice(0, -2)
|
||||||
|
};
|
||||||
|
} else if (lastIsCap) {
|
||||||
|
{
|
||||||
|
name: parts[-1],
|
||||||
|
pack: parts.slice(0, -1)
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
throw 'Type path $path should end with a capitalized type';
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -127,4 +127,8 @@ class BasicTestCase extends Test {
|
|||||||
seasonsGreetings += "ho ";
|
seasonsGreetings += "ho ";
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testTypedDefvar() {
|
||||||
|
Assert.equals(8, BasicTestCase.myInt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -63,12 +63,14 @@
|
|||||||
(defvar myIf7 (if "string" true false))
|
(defvar myIf7 (if "string" true false))
|
||||||
(defvar myIf8 (if "" true false))
|
(defvar myIf8 (if "" true false))
|
||||||
|
|
||||||
|
(defvar :Int myInt 8)
|
||||||
|
|
||||||
(defmacrofun doTwiceInt [intOp]
|
(defmacrofun doTwiceInt [intOp]
|
||||||
,intOp
|
,intOp
|
||||||
,intOp)
|
,intOp)
|
||||||
|
|
||||||
// I think this causes doTwiceInt's runtime function to be typed as requiring Quote<Int> and returning Int
|
// I think this causes doTwiceInt's runtime function to be typed as requiring Quote<Int> and returning Int
|
||||||
(defun incrementTwice [:Int val]
|
(defun :Int incrementTwice [:Int val]
|
||||||
(doTwiceInt ++val))
|
(doTwiceInt ++val))
|
||||||
|
|
||||||
(defmacrofun doTwiceString [stringOp]
|
(defmacrofun doTwiceString [stringOp]
|
||||||
|
Reference in New Issue
Block a user