Type annotations.
This commit is contained in:
parent
e3a7dc002f
commit
49ee777c50
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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: {
|
||||
|
@ -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.");
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.");
|
||||
|
||||
|
@ -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__);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -28,6 +28,7 @@ typedef enum {
|
||||
TOKEN_BANG,
|
||||
TOKEN_GREATER,
|
||||
TOKEN_LESS,
|
||||
TOKEN_ARROW, /* -> */
|
||||
|
||||
/* Comparisons */
|
||||
TOKEN_GREATER_EQUAL,
|
||||
|
9
src/vm.c
9
src/vm.c
@ -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
24
test/testAnnotations.krk
Normal 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))
|
9
test/testAnnotations.krk.expect
Normal file
9
test/testAnnotations.krk.expect
Normal 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
|
Loading…
x
Reference in New Issue
Block a user