Type annotations.

This commit is contained in:
K Lange 2021-03-11 20:44:39 +09:00
parent e3a7dc002f
commit 49ee777c50
15 changed files with 123 additions and 10 deletions

View File

@ -66,7 +66,8 @@ typedef enum {
OP_INVOKE_CONTAINS,
OP_BREAKPOINT, /* NEVER output this instruction in the compiler or bad things can happen */
OP_YIELD,
/* current highest: 44 */
OP_ANNOTATE,
/* current highest: 45 */
OP_CALL = 64,
OP_CLASS,

View File

@ -130,6 +130,8 @@ typedef struct Compiler {
size_t localNameCapacity;
struct IndexWithNext * properties;
struct Compiler * enclosed;
size_t annotationCount;
} Compiler;
typedef struct ClassCompiler {
@ -203,6 +205,8 @@ static void initCompiler(Compiler * compiler, FunctionType type) {
compiler->loopLocalCount = 0;
compiler->localNameCapacity = 0;
compiler->properties = NULL;
compiler->enclosed = NULL;
compiler->annotationCount = 0;
if (type != TYPE_MODULE) {
current->function->name = krk_copyString(parser.previous.start, parser.previous.length);
@ -925,6 +929,26 @@ static void doUpvalues(Compiler * compiler, KrkFunction * function) {
}
}
static void typeHint(KrkToken name) {
current->enclosing->enclosed = current;
current = current->enclosing;
if (!current->enclosed->annotationCount) {
KrkToken dictOf = syntheticToken("dictOf");
size_t ind = identifierConstant(&dictOf);
EMIT_CONSTANT_OP(OP_GET_GLOBAL, ind);
}
current->enclosed->annotationCount++;
/* Emit name */
emitConstant(OBJECT_VAL(krk_copyString(name.start, name.length)));
expression();
current = current->enclosed;
current->enclosing->enclosed = NULL;
}
static void function(FunctionType type, size_t blockWidth) {
Compiler compiler;
initCompiler(&compiler, type);
@ -944,6 +968,11 @@ static void function(FunctionType type, size_t blockWidth) {
if (!isMethod(type)) {
error("Invalid use of `self` as a function paramenter.");
}
if (check(TOKEN_COLON)) {
KrkToken name = parser.previous;
match(TOKEN_COLON);
typeHint(name);
}
continue;
}
if (match(TOKEN_ASTERISK) || check(TOKEN_POW)) {
@ -965,6 +994,11 @@ static void function(FunctionType type, size_t blockWidth) {
/* Collect a name, specifically "args" or "kwargs" are commont */
ssize_t paramConstant = parseVariable("Expect parameter name.");
defineVariable(paramConstant);
if (check(TOKEN_COLON)) {
KrkToken name = parser.previous;
match(TOKEN_COLON);
typeHint(name);
}
/* Make that a valid local for this function */
size_t myLocal = current->localCount - 1;
EMIT_CONSTANT_OP(OP_GET_LOCAL, myLocal);
@ -991,6 +1025,11 @@ static void function(FunctionType type, size_t blockWidth) {
}
ssize_t paramConstant = parseVariable("Expect parameter name.");
defineVariable(paramConstant);
if (check(TOKEN_COLON)) {
KrkToken name = parser.previous;
match(TOKEN_COLON);
typeHint(name);
}
if (match(TOKEN_EQUAL)) {
/*
* We inline default arguments by checking if they are equal
@ -1026,13 +1065,24 @@ static void function(FunctionType type, size_t blockWidth) {
stopEatingWhitespace();
consume(TOKEN_RIGHT_PAREN, "Expected end of parameter list.");
if (match(TOKEN_ARROW)) {
typeHint(syntheticToken("return"));
}
consume(TOKEN_COLON, "Expected colon after function signature.");
block(blockWidth,"def");
KrkFunction * function = endCompiler();
if (compiler.annotationCount) {
EMIT_CONSTANT_OP(OP_CALL,compiler.annotationCount * 2);
}
size_t ind = krk_addConstant(currentChunk(), OBJECT_VAL(function));
EMIT_CONSTANT_OP(OP_CLOSURE, ind);
doUpvalues(&compiler, function);
if (compiler.annotationCount) {
emitByte(OP_ANNOTATE);
}
freeCompiler(&compiler);
}
@ -1082,14 +1132,12 @@ static void method(size_t blockWidth) {
}
#define ATTACH_PROPERTY(propName,how,propValue) do { \
emitBytes(OP_DUP, 0); \
KrkToken val_tok = syntheticToken(propValue); \
size_t val_ind = identifierConstant(&val_tok); \
EMIT_CONSTANT_OP(how, val_ind); \
KrkToken name_tok = syntheticToken(propName); \
size_t name_ind = identifierConstant(&name_tok); \
EMIT_CONSTANT_OP(OP_SET_PROPERTY, name_ind); \
emitByte(OP_POP); \
EMIT_CONSTANT_OP(OP_CLASS_PROPERTY, name_ind); \
} while (0)
static KrkToken classDeclaration() {
@ -2902,6 +2950,7 @@ KrkFunction * krk_compile(const char * src, char * fileName) {
void krk_markCompilerRoots() {
Compiler * compiler = current;
while (compiler != NULL) {
if (compiler->enclosed) krk_markObject((KrkObj*)compiler->enclosed->function);
krk_markObject((KrkObj*)compiler->function);
compiler = compiler->enclosing;
}

View File

@ -132,6 +132,7 @@ static void blackenObject(KrkObj * object) {
for (size_t i = 0; i < closure->upvalueCount; ++i) {
krk_markObject((KrkObj*)closure->upvalues[i]);
}
krk_markValue(closure->annotations);
break;
}
case OBJ_FUNCTION: {

View File

@ -386,6 +386,7 @@ void _createAndBind_dictClass(void) {
BIND_METHOD(dict,setdefault);
BIND_METHOD(dict,update);
krk_defineNative(&dict->methods, ".__str__", FUNC_NAME(dict,__repr__));
krk_defineNative(&dict->methods, ".__class_getitem__", KrkGenericAlias);
krk_finalizeClass(dict);
KRK_DOC(dict, "Mapping of arbitrary keys to values.");

View File

@ -139,6 +139,12 @@ KRK_METHOD(function,__args__,{
return OBJECT_VAL(tuple);
})
KRK_METHOD(function,__annotations__,{
METHOD_TAKES_NONE();
if (!IS_CLOSURE(self)) return NONE_VAL();
return AS_CLOSURE(self)->annotations;
})
#undef CURRENT_CTYPE
#define CURRENT_CTYPE KrkFunction*
@ -223,6 +229,12 @@ KRK_METHOD(method,__doc__,{
return FUNC_NAME(function,__doc__)(1,(KrkValue[]){OBJECT_VAL(self->method)},0);
})
KRK_METHOD(method,__annotations__,{
METHOD_TAKES_NONE();
return FUNC_NAME(function,__annotations__)(1,(KrkValue[]){OBJECT_VAL(self->method)},0);
})
KRK_FUNC(staticmethod,{
FUNCTION_TAKES_EXACTLY(1);
CHECK_ARG(0,CLOSURE,KrkClosure*,method);
@ -232,6 +244,7 @@ KRK_FUNC(staticmethod,{
for (size_t i = 0; i < method->upvalueCount; ++i) {
AS_CLOSURE(krk_peek(0))->upvalues[i] = method->upvalues[i];
}
AS_CLOSURE(krk_peek(0))->annotations = method->annotations;
AS_CLOSURE(krk_peek(0))->isStaticMethod = 1;
return krk_pop();
})
@ -245,6 +258,7 @@ KRK_FUNC(classmethod,{
for (size_t i = 0; i < method->upvalueCount; ++i) {
AS_CLOSURE(krk_peek(0))->upvalues[i] = method->upvalues[i];
}
AS_CLOSURE(krk_peek(0))->annotations = method->annotations;
AS_CLOSURE(krk_peek(0))->isClassMethod = 1;
return krk_pop();
})
@ -266,6 +280,7 @@ void _createAndBind_functionClass(void) {
BIND_PROP(function,__qualname__);
BIND_PROP(function,__file__);
BIND_PROP(function,__args__);
BIND_PROP(function,__annotations__);
krk_defineNative(&function->methods, ".__repr__", FUNC_NAME(function,__str__));
krk_finalizeClass(function);
@ -277,6 +292,7 @@ void _createAndBind_functionClass(void) {
BIND_PROP(method,__qualname__);
BIND_PROP(method,__file__);
BIND_PROP(method,__args__);
BIND_PROP(method,__annotations__);
krk_defineNative(&method->methods, ".__repr__", FUNC_NAME(method,__str__));
krk_finalizeClass(method);

View File

@ -472,8 +472,6 @@ static KrkValue _reversed(int argc, KrkValue argv[], int hasKw) {
return krk_pop();
}
extern NativeFn GenericAlias;
_noexport
void _createAndBind_listClass(void) {
KrkClass * list = ADD_BASE_CLASS(vm.baseClasses->listClass, "list", vm.baseClasses->objectClass);
@ -505,7 +503,7 @@ void _createAndBind_listClass(void) {
BIND_METHOD(list,sort);
krk_defineNative(&list->methods, ".__delitem__", FUNC_NAME(list,pop));
krk_defineNative(&list->methods, ".__str__", FUNC_NAME(list,__repr__));
krk_defineNative(&list->methods, ".__class_getitem__", GenericAlias);
krk_defineNative(&list->methods, ".__class_getitem__", KrkGenericAlias);
krk_finalizeClass(list);
KRK_DOC(list, "Mutable sequence of arbitrary values.");

View File

@ -64,4 +64,4 @@ KRK_FUNC(__class_getitem__,{
return finishStringBuilder(&sb);
})
NativeFn GenericAlias = FUNC_NAME(krk,__class_getitem__);
NativeFn KrkGenericAlias = FUNC_NAME(krk,__class_getitem__);

View File

@ -266,6 +266,7 @@ KrkClosure * krk_newClosure(KrkFunction * function) {
closure->function = function;
closure->upvalues = upvalues;
closure->upvalueCount = function->upvalueCount;
closure->annotations = krk_dict_of(0,NULL,0);
return closure;
}

View File

@ -141,6 +141,7 @@ typedef struct {
size_t upvalueCount;
unsigned char isClassMethod:1;
unsigned char isStaticMethod:1;
KrkValue annotations;
} KrkClosure;
typedef void (*KrkCleanupCallback)(struct KrkInstance *);
@ -363,6 +364,7 @@ extern uint32_t krk_unicodeCodepoint(KrkString * string, size_t index);
extern size_t krk_codepointToBytes(krk_integer_type value, unsigned char * out);
/* Internal stuff. */
extern NativeFn KrkGenericAlias;
extern KrkFunction * krk_newFunction(void);
extern KrkNative * krk_newNative(NativeFn function, const char * name, int type);
extern KrkClosure * krk_newClosure(KrkFunction * function);

View File

@ -40,6 +40,7 @@ SIMPLE(OP_CLEANUP_WITH)
SIMPLE(OP_FILTER_EXCEPT)
SIMPLE(OP_BREAKPOINT)
SIMPLE(OP_YIELD)
SIMPLE(OP_ANNOTATE)
CONSTANT(OP_DEFINE_GLOBAL,(void)0)
CONSTANT(OP_CONSTANT,(void)0)
CONSTANT(OP_GET_GLOBAL,(void)0)

View File

@ -353,7 +353,7 @@ KrkToken krk_scanToken() {
case '=': return makeToken(match('=') ? TOKEN_EQUAL_EQUAL : TOKEN_EQUAL);
case '<': return makeToken(match('=') ? TOKEN_LESS_EQUAL : (match('<') ? (match('=') ? TOKEN_LSHIFT_EQUAL : TOKEN_LEFT_SHIFT) : TOKEN_LESS));
case '>': return makeToken(match('=') ? TOKEN_GREATER_EQUAL : (match('>') ? (match('=') ? TOKEN_RSHIFT_EQUAL : TOKEN_RIGHT_SHIFT) : TOKEN_GREATER));
case '-': return makeToken(match('=') ? TOKEN_MINUS_EQUAL : (match('-') ? TOKEN_MINUS_MINUS : TOKEN_MINUS));
case '-': return makeToken(match('=') ? TOKEN_MINUS_EQUAL : (match('-') ? TOKEN_MINUS_MINUS : (match('>') ? TOKEN_ARROW : TOKEN_MINUS)));
case '+': return makeToken(match('=') ? TOKEN_PLUS_EQUAL : (match('+') ? TOKEN_PLUS_PLUS : TOKEN_PLUS));
case '^': return makeToken(match('=') ? TOKEN_CARET_EQUAL : TOKEN_CARET);
case '|': return makeToken(match('=') ? TOKEN_PIPE_EQUAL : TOKEN_PIPE);

View File

@ -28,6 +28,7 @@ typedef enum {
TOKEN_BANG,
TOKEN_GREATER,
TOKEN_LESS,
TOKEN_ARROW, /* -> */
/* Comparisons */
TOKEN_GREATER_EQUAL,

View File

@ -2202,6 +2202,15 @@ _resumeHook: (void)0;
/* Do NOT restore the stack */
return result;
}
case OP_ANNOTATE: {
if (!IS_CLOSURE(krk_peek(0))) {
krk_runtimeError(vm.exceptions->typeError, "Can not annotate '%s'.", krk_typeName(krk_peek(0)));
goto _finishException;
}
krk_swap(1);
AS_CLOSURE(krk_peek(1))->annotations = krk_pop();
break;
}
/*
* Two-byte operands

24
test/testAnnotations.krk Normal file
View File

@ -0,0 +1,24 @@
def foo(a: int, b: float) -> list:
print("I am a function.")
return [42]
print(foo(1,2.0))
print(foo.__annotations__)
# Okay, now let's try some methods
class Foo:
def amethod(self: Foo, anint: int, adict: dict[str,object]) -> None:
print("I am a method taking a dict.")
@staticmethod
def astatic(abool: bool, astr: str):
print("I return a Foo? Amazing!")
return Foo()
print(Foo().amethod.__annotations__)
print(Foo().amethod(1,{'two': 3}))
print(Foo.astatic.__annotations__)
print(isinstance(Foo.astatic(True,"yes"),Foo))

View File

@ -0,0 +1,9 @@
I am a function.
[42]
{'return': <class 'list'>, 'a': <class 'int'>, 'b': <class 'float'>}
{'return': None, 'anint': <class 'int'>, 'self': <class '__main__.Foo'>, 'adict': 'dict[str,object]'}
I am a method taking a dict.
None
{'astr': <class 'str'>, 'abool': <class 'bool'>}
I return a Foo? Amazing!
True