diff --git a/src/kiss/FieldForms.hx b/src/kiss/FieldForms.hx index fa76c371..9f1d630b 100644 --- a/src/kiss/FieldForms.hx +++ b/src/kiss/FieldForms.hx @@ -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)))) diff --git a/src/kiss/Helpers.hx b/src/kiss/Helpers.hx index 729f67b7..5d5b7185 100644 --- a/src/kiss/Helpers.hx +++ b/src/kiss/Helpers.hx @@ -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 = path.split("."); + var uppercaseParts:List = 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'; + }); + } } diff --git a/src/test/cases/BasicTestCase.hx b/src/test/cases/BasicTestCase.hx index 679eb324..0fc5004c 100644 --- a/src/test/cases/BasicTestCase.hx +++ b/src/test/cases/BasicTestCase.hx @@ -127,4 +127,8 @@ class BasicTestCase extends Test { seasonsGreetings += "ho "; })); } + + function testTypedDefvar() { + Assert.equals(8, BasicTestCase.myInt); + } } diff --git a/src/test/cases/BasicTestCase.kiss b/src/test/cases/BasicTestCase.kiss index d92e2343..4f7a894d 100644 --- a/src/test/cases/BasicTestCase.kiss +++ b/src/test/cases/BasicTestCase.kiss @@ -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 and returning Int -(defun incrementTwice [:Int val] +(defun :Int incrementTwice [:Int val] (doTwiceInt ++val)) (defmacrofun doTwiceString [stringOp]