Change 'let' semantics to do unpacking; support unpacking more things
This commit is contained in:
parent
d2d1c98a1e
commit
13bbc3ae2d
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
*.o
|
||||
*.so
|
||||
kuroko
|
||||
wasm/
|
||||
|
13
README.md
13
README.md
@ -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
|
||||
|
4
chunk.h
4
chunk.h
@ -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;
|
||||
|
83
compiler.c
83
compiler.c
@ -614,16 +614,69 @@ static void expression() {
|
||||
parsePrecedence(PREC_ASSIGNMENT);
|
||||
}
|
||||
|
||||
static void varDeclaration() {
|
||||
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)) {
|
||||
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);
|
||||
|
2
debug.c
2
debug.c
@ -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,+)
|
||||
|
@ -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
97
vm.c
@ -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
4
vm.h
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user