Change 'let' semantics to do unpacking; support unpacking more things

This commit is contained in:
K. Lange 2021-01-14 21:16:48 +09:00
parent d2d1c98a1e
commit 13bbc3ae2d
8 changed files with 153 additions and 61 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
*.o
*.so
kuroko
wasm/

View File

@ -253,14 +253,23 @@ print(foo)
# → 1
```
You may declare and define multiple variables on a single line:
You may declare and define multiple variables on a single line as follows:
```py
let a = 1, b = "test", c = object()
let a, b, c = 1, "test", object()
print(a,b,c)
# → 1 test <instance of object at ...>
```
The `let` statement can also be used to unpack some sequence types:
```py
let t = (1, 2, 3)
let a, b, c = t
print(a,b,c)
# → 1 2 3
```
_**Note:** Identifier names, including for variables, functions, and classes, can be Unicode sequences. All non-ASCII codepoints are accepted as identifier characters._
### Assignments

View File

@ -71,7 +71,7 @@ typedef enum {
OP_EXPAND_ARGS,
OP_FINALIZE,
OP_TUPLE,
OP_UNPACK_TUPLE,
OP_UNPACK,
OP_PUSH_WITH,
OP_CLEANUP_WITH,
@ -100,7 +100,7 @@ typedef enum {
OP_INC_LONG,
OP_KWARGS_LONG,
OP_TUPLE_LONG,
OP_UNPACK_TUPLE_LONG,
OP_UNPACK_LONG,
OP_DEL_GLOBAL_LONG,
OP_DEL_PROPERTY_LONG,
} KrkOpCode;

View File

@ -614,16 +614,69 @@ static void expression() {
parsePrecedence(PREC_ASSIGNMENT);
}
static void varDeclaration() {
ssize_t ind = parseVariable("Expected variable name.");
static void letDeclaration(void) {
size_t argCount = 0;
size_t argSpace = 1;
ssize_t * args = GROW_ARRAY(ssize_t,NULL,0,1);
do {
if (argSpace < argCount + 1) {
size_t old = argSpace;
argSpace = GROW_CAPACITY(old);
args = GROW_ARRAY(ssize_t,args,old,argSpace);
}
ssize_t ind = parseVariable("Expected variable name.");
if (current->scopeDepth > 0) {
/* Need locals space */
args[argCount++] = current->localCount - 1;
emitByte(OP_NONE);
defineVariable(ind);
} else {
args[argCount++] = ind;
}
} while (match(TOKEN_COMMA));
if (match(TOKEN_EQUAL)) {
expression();
} else {
emitByte(OP_NONE);
size_t expressionCount = 0;
do {
expressionCount++;
expression();
} while (match(TOKEN_COMMA));
if (expressionCount == 1 && argCount > 1) {
EMIT_CONSTANT_OP(OP_UNPACK, argCount);
} else if (expressionCount == 1 && argCount == 1) {
/* Do nothing */
} else if (expressionCount == argCount) {
/* This is stupid but it flips the stuff around */
EMIT_CONSTANT_OP(OP_TUPLE, argCount);
EMIT_CONSTANT_OP(OP_UNPACK, argCount);
} else if (expressionCount > 1 && argCount == 1) {
EMIT_CONSTANT_OP(OP_TUPLE, expressionCount);
} else {
error("Invalid sequence unpack in 'let' statement");
}
if (current->scopeDepth > 0) {
for (size_t i = argCount; i > 0; i--) {
EMIT_CONSTANT_OP(OP_SET_LOCAL, args[i-1]);
emitByte(OP_POP);
}
}
} else if (current->scopeDepth == 0) {
/* Need to nil it */
for (size_t i = 0; i < argCount; ++i) {
emitByte(OP_NONE);
}
}
defineVariable(ind);
if (current->scopeDepth == 0) {
for (size_t i = argCount; i > 0; i--) {
defineVariable(args[i-1]);
}
}
if (!match(TOKEN_EOL) && !match(TOKEN_EOF)) {
error("Expected end of line after 'let' statement.");
}
}
static void synchronize() {
@ -651,12 +704,7 @@ static void declaration() {
if (check(TOKEN_DEF)) {
defDeclaration();
} else if (match(TOKEN_LET)) {
do {
varDeclaration();
} while (match(TOKEN_COMMA));
if (!match(TOKEN_EOL) && !match(TOKEN_EOF)) {
error("Expected EOL after variable declaration.\n");
}
letDeclaration();
} else if (check(TOKEN_CLASS)) {
classDeclaration();
} else if (check(TOKEN_AT)) {
@ -1257,15 +1305,23 @@ static void forStatement() {
ssize_t loopInd = current->localCount;
ssize_t varCount = 0;
int matchedEquals = 0;
do {
varDeclaration();
ssize_t ind = parseVariable("Expected name for loop iterator.");
if (match(TOKEN_EQUAL)) {
matchedEquals = 1;
expression();
} else {
emitByte(OP_NONE);
}
defineVariable(ind);
varCount++;
} while (match(TOKEN_COMMA));
int loopStart;
int exitJump;
if (match(TOKEN_IN)) {
if (!matchedEquals && match(TOKEN_IN)) {
/* ITERABLE.__iter__() */
beginScope();
@ -1303,7 +1359,7 @@ static void forStatement() {
if (varCount > 1) {
EMIT_CONSTANT_OP(OP_GET_LOCAL, loopInd);
EMIT_CONSTANT_OP(OP_UNPACK_TUPLE, varCount);
EMIT_CONSTANT_OP(OP_UNPACK, varCount);
for (ssize_t i = loopInd + varCount - 1; i >= loopInd; i--) {
EMIT_CONSTANT_OP(OP_SET_LOCAL, i);
emitByte(OP_POP);
@ -1790,7 +1846,8 @@ static void list(int canAssign) {
ssize_t loopInd = current->localCount;
ssize_t varCount = 0;
do {
varDeclaration();
defineVariable(parseVariable("Expected name for iteration variable."));
emitByte(OP_NONE);
defineVariable(loopInd);
varCount++;
} while (match(TOKEN_COMMA));
@ -1838,7 +1895,7 @@ static void list(int canAssign) {
/* Unpack tuple */
if (varCount > 1) {
EMIT_CONSTANT_OP(OP_GET_LOCAL, loopInd);
EMIT_CONSTANT_OP(OP_UNPACK_TUPLE, varCount);
EMIT_CONSTANT_OP(OP_UNPACK, varCount);
for (ssize_t i = loopInd + varCount - 1; i >= loopInd; i--) {
EMIT_CONSTANT_OP(OP_SET_LOCAL, i);
emitByte(OP_POP);

View File

@ -144,7 +144,7 @@ size_t krk_disassembleInstruction(FILE * f, KrkFunction * func, size_t offset) {
OPERAND(OP_CALL, (void)0)
OPERAND(OP_INC, (void)0)
OPERAND(OP_TUPLE, (void)0)
OPERAND(OP_UNPACK_TUPLE, (void)0)
OPERAND(OP_UNPACK, (void)0)
JUMP(OP_JUMP,+)
JUMP(OP_JUMP_IF_FALSE,+)
JUMP(OP_JUMP_IF_TRUE,+)

View File

@ -4,7 +4,7 @@ let lines = f.read().split('\n')[:-1]
def count_trees(lines, x_off, y_off):
let modulo = len(lines[0])
let x = 0, y = 0, trees = 0
let x, y, trees = 0, 0, 0
while y < len(lines):
if lines[y][x % modulo] == '#':
trees += 1

97
vm.c
View File

@ -443,6 +443,16 @@ static KrkValue _dict_key_at_index(int argc, KrkValue argv[]) {
return OBJECT_VAL(outValue);
}
static KrkValue _dict_nth_key_fast(size_t capacity, KrkTableEntry * entries, size_t index) {
size_t found = 0;
for (size_t i = 0; i < capacity; ++i) {
if (IS_KWARGS(entries[i].key)) continue;
if (found == index) return entries[i].key;
found++;
}
return NONE_VAL();
}
/**
* list.__init__()
*/
@ -3323,33 +3333,31 @@ void krk_initVM(int flags) {
* the list and dict types by pulling them out of the global namespace,
* as they were exported by builtins.krk */
krk_tableGet(&vm.builtins->fields,OBJECT_VAL(S("list")),&val);
KrkClass * _class = AS_CLASS(val);
krk_defineNative(&_class->methods, ".__init__", _list_init);
krk_defineNative(&_class->methods, ".__get__", _list_get);
krk_defineNative(&_class->methods, ".__set__", _list_set);
krk_defineNative(&_class->methods, ".__delitem__", _list_pop);
krk_defineNative(&_class->methods, ".__len__", _list_len);
krk_defineNative(&_class->methods, ".__contains__", _list_contains);
krk_defineNative(&_class->methods, ".__getslice__", _list_slice);
krk_defineNative(&_class->methods, ".__iter__", _list_iter);
krk_defineNative(&_class->methods, ".append", _list_append);
krk_defineNative(&_class->methods, ".pop", _list_pop);
krk_defineNative(&_class->methods, "._extend_fast", _list_extend_fast);
krk_finalizeClass(_class);
vm.baseClasses.listClass = AS_CLASS(val);
krk_defineNative(&vm.baseClasses.listClass->methods, ".__init__", _list_init);
krk_defineNative(&vm.baseClasses.listClass->methods, ".__get__", _list_get);
krk_defineNative(&vm.baseClasses.listClass->methods, ".__set__", _list_set);
krk_defineNative(&vm.baseClasses.listClass->methods, ".__delitem__", _list_pop);
krk_defineNative(&vm.baseClasses.listClass->methods, ".__len__", _list_len);
krk_defineNative(&vm.baseClasses.listClass->methods, ".__contains__", _list_contains);
krk_defineNative(&vm.baseClasses.listClass->methods, ".__getslice__", _list_slice);
krk_defineNative(&vm.baseClasses.listClass->methods, ".__iter__", _list_iter);
krk_defineNative(&vm.baseClasses.listClass->methods, ".append", _list_append);
krk_defineNative(&vm.baseClasses.listClass->methods, ".pop", _list_pop);
krk_defineNative(&vm.baseClasses.listClass->methods, "._extend_fast", _list_extend_fast);
krk_finalizeClass(vm.baseClasses.listClass);
krk_tableGet(&vm.builtins->fields,OBJECT_VAL(S("dict")),&val);
_class = AS_CLASS(val);
krk_defineNative(&_class->methods, ".__init__", _dict_init);
krk_defineNative(&_class->methods, ".__get__", _dict_get);
krk_defineNative(&_class->methods, ".__set__", _dict_set);
krk_defineNative(&_class->methods, ".__delitem__", _dict_delitem);
krk_defineNative(&_class->methods, ".__len__", _dict_len);
krk_defineNative(&_class->methods, ".__contains__", _dict_contains);
krk_finalizeClass(_class);
/* These are used to for dict.keys() to create the iterators. */
krk_defineNative(&_class->methods, ".capacity", _dict_capacity);
krk_defineNative(&_class->methods, "._key_at_index", _dict_key_at_index);
vm.baseClasses.dictClass = AS_CLASS(val);
krk_defineNative(&vm.baseClasses.dictClass->methods, ".__init__", _dict_init);
krk_defineNative(&vm.baseClasses.dictClass->methods, ".__get__", _dict_get);
krk_defineNative(&vm.baseClasses.dictClass->methods, ".__set__", _dict_set);
krk_defineNative(&vm.baseClasses.dictClass->methods, ".__delitem__", _dict_delitem);
krk_defineNative(&vm.baseClasses.dictClass->methods, ".__len__", _dict_len);
krk_defineNative(&vm.baseClasses.dictClass->methods, ".__contains__", _dict_contains);
krk_defineNative(&vm.baseClasses.dictClass->methods, ".capacity", _dict_capacity);
krk_defineNative(&vm.baseClasses.dictClass->methods, "._key_at_index", _dict_key_at_index);
krk_finalizeClass(vm.baseClasses.dictClass);
}
/* The VM is now ready to start executing code. */
@ -4192,22 +4200,35 @@ static KrkValue run() {
}
break;
}
case OP_UNPACK_TUPLE_LONG:
case OP_UNPACK_TUPLE: {
case OP_UNPACK_LONG:
case OP_UNPACK: {
size_t count = readBytes(frame, operandWidth);
KrkValue tuple = krk_peek(0);
if (!IS_TUPLE(tuple)) {
krk_runtimeError(vm.exceptions.typeError, "Can not unpack non-tuple '%s'", krk_typeName(tuple));
goto _finishException;
} else if (AS_TUPLE(tuple)->values.count != count) {
krk_runtimeError(vm.exceptions.valueError, "Wrong number of values to unpack (wanted %d, got %d)", (int)count, (int)AS_TUPLE(tuple)->values.count);
KrkValue sequence = krk_peek(0);
/* First figure out what it is and if we can unpack it. */
#define unpackArray(counter, indexer) do { \
if (counter != count) { \
krk_runtimeError(vm.exceptions.valueError, "Wrong number of values to unpack (wanted %d, got %d)", (int)count, (int)counter); \
} \
for (size_t i = 1; i < counter; ++i) { \
krk_push(indexer); \
} \
size_t i = 0; \
vm.stackTop[-count] = indexer; \
} while (0)
if (IS_TUPLE(sequence)) {
unpackArray(AS_TUPLE(sequence)->values.count, AS_TUPLE(sequence)->values.values[i]);
} else if (IS_INSTANCE(sequence) && AS_INSTANCE(sequence)->_class == vm.baseClasses.listClass) {
KrkValue _list_internal = OBJECT_VAL(AS_INSTANCE(sequence)->_internal);
unpackArray(AS_LIST(_list_internal)->count, AS_LIST(_list_internal)->values[i]);
} else if (IS_INSTANCE(sequence) && AS_INSTANCE(sequence)->_class == vm.baseClasses.dictClass) {
KrkValue _dict_internal = OBJECT_VAL(AS_INSTANCE(sequence)->_internal);
unpackArray(AS_DICT(_dict_internal)->count, _dict_nth_key_fast(AS_DICT(_dict_internal)->capacity, AS_DICT(_dict_internal)->entries, i));
} else if (IS_STRING(sequence)) {
unpackArray(AS_STRING(sequence)->codesLength, _string_get(2,(KrkValue[]){sequence,INTEGER_VAL(i)}));
} else {
krk_runtimeError(vm.exceptions.notImplementedError, "Can not iterate '%s' in unpack.", krk_typeName(sequence));
goto _finishException;
}
/* Unpack from 1 to end, then unpack 0 into bottom slot */
for (size_t i = 1; i < AS_TUPLE(tuple)->values.count; ++i) {
krk_push(AS_TUPLE(tuple)->values.values[i]);
}
vm.stackTop[-count] = AS_TUPLE(tuple)->values.values[0];
break;
}
case OP_PUSH_WITH: {

4
vm.h
View File

@ -104,6 +104,10 @@ typedef struct {
KrkClass * rangeiteratorClass;
KrkClass * striteratorClass;
KrkClass * tupleiteratorClass;
/* These are actually defined in builtins.krk and are real instances */
KrkClass * listClass;
KrkClass * dictClass;
} baseClasses;
KrkValue currentException;