Allow type hints on functions and vars

This commit is contained in:
2020-11-18 15:52:30 -07:00
parent ff389ecf25
commit bb97388247
4 changed files with 59 additions and 7 deletions

View File

@@ -4,6 +4,7 @@ import haxe.macro.Expr;
import haxe.macro.Context;
import kiss.Reader;
import kiss.Types;
import kiss.Helpers;
using StringTools;
@@ -34,10 +35,10 @@ class FieldForms {
static function fieldName(formName:String, position:String, nameExp:ReaderExp) {
return switch (nameExp) {
case Symbol(name):
case Symbol(name) | TypedExp(_, Symbol(name)):
name;
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 {
name: name,
access: access,
kind: FVar(null, // TODO allow type anotations
convert(args[1])),
kind: FVar(switch (args[0]) {
case TypedExp(type, _):
Helpers.parseTypePath(type);
default: null;
}, convert(args[1])),
pos: Context.currentPos()
};
}
@@ -70,10 +74,12 @@ class FieldForms {
return {
name: name,
access: access,
// TODO type parameter declarations
kind: FFun({
args: switch (args[1]) {
case ListExp(funcArgs):
[
// TODO optional arguments, default values
for (funcArg in funcArgs)
{
name: switch (funcArg) {
@@ -82,7 +88,11 @@ class FieldForms {
default:
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(_, _):
@@ -90,7 +100,10 @@ class FieldForms {
default:
throw '${args[1]} should be an argument list';
},
ret: null,
ret: switch (args[0]) {
case TypedExp(type, _): Helpers.parseTypePath(type);
default: null;
},
expr: {
pos: Context.currentPos(),
expr: EReturn(convert(CallExp(Symbol("begin"), args.slice(2))))

View File

@@ -3,6 +3,8 @@ package kiss;
import haxe.macro.Expr;
import haxe.macro.Context;
using StringTools;
class Helpers {
public static function withPos(e:ExprDef):Expr {
return {
@@ -10,4 +12,35 @@ class Helpers {
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';
});
}
}

View File

@@ -127,4 +127,8 @@ class BasicTestCase extends Test {
seasonsGreetings += "ho ";
}));
}
function testTypedDefvar() {
Assert.equals(8, BasicTestCase.myInt);
}
}

View File

@@ -63,12 +63,14 @@
(defvar myIf7 (if "string" true false))
(defvar myIf8 (if "" true false))
(defvar :Int myInt 8)
(defmacrofun doTwiceInt [intOp]
,intOp
,intOp)
// 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))
(defmacrofun doTwiceString [stringOp]