Add 'del' statement.

This commit is contained in:
K. Lange 2021-01-14 16:00:43 +09:00
parent a9b25d26ee
commit d2d1c98a1e
11 changed files with 204 additions and 7 deletions

View File

@ -77,6 +77,10 @@ typedef enum {
OP_IS,
OP_DEL_GLOBAL,
OP_DEL_PROPERTY,
OP_INVOKE_DELETE,
OP_CONSTANT_LONG = 128,
OP_DEFINE_GLOBAL_LONG,
OP_GET_GLOBAL_LONG,
@ -97,6 +101,8 @@ typedef enum {
OP_KWARGS_LONG,
OP_TUPLE_LONG,
OP_UNPACK_TUPLE_LONG,
OP_DEL_GLOBAL_LONG,
OP_DEL_PROPERTY_LONG,
} KrkOpCode;
typedef struct {

View File

@ -124,6 +124,7 @@ typedef struct ClassCompiler {
static Parser parser;
static Compiler * current = NULL;
static ClassCompiler * currentClass = NULL;
static int inDel = 0;
static KrkChunk * currentChunk() {
return &current->function->chunk;
@ -477,6 +478,10 @@ static int matchAssignment(void) {
match(TOKEN_PLUS_PLUS) || match(TOKEN_MINUS_MINUS);
}
static int matchEndOfDel(void) {
return check(TOKEN_COMMA) || match(TOKEN_EOL) || match(TOKEN_EOF);
}
static void assignmentValue(void) {
switch (parser.previous.type) {
case TOKEN_PLUS_EQUAL:
@ -521,7 +526,9 @@ static void get_(int canAssign) {
consume(TOKEN_RIGHT_SQUARE, "Expected ending square bracket after slice.");
}
if (canAssign && matchAssignment()) {
error("Can not assign to slice.");
error("Slice assignment not implemented.");
} else if (inDel && matchEndOfDel()) {
error("Slice deletion not implemented.");
} else {
emitByte(OP_INVOKE_GETSLICE);
}
@ -536,6 +543,13 @@ static void get_(int canAssign) {
emitByte(OP_INVOKE_GETTER); /* o e v */
assignmentValue(); /* o e v a */
emitByte(OP_INVOKE_SETTER); /* r */
} else if (inDel && matchEndOfDel()) {
if (!canAssign || inDel != 1) {
error("Invalid del target");
} else if (canAssign) {
emitByte(OP_INVOKE_DELETE);
inDel = 2;
}
} else {
emitByte(OP_INVOKE_GETTER);
}
@ -553,6 +567,13 @@ static void dot(int canAssign) {
EMIT_CONSTANT_OP(OP_GET_PROPERTY, ind);
assignmentValue();
EMIT_CONSTANT_OP(OP_SET_PROPERTY, ind);
} else if (inDel && matchEndOfDel()) {
if (!canAssign || inDel != 1) {
error("Invalid del target");
} else {
EMIT_CONSTANT_OP(OP_DEL_PROPERTY, ind);
inDel = 2;
}
} else {
EMIT_CONSTANT_OP(OP_GET_PROPERTY, ind);
}
@ -1449,6 +1470,13 @@ static void statement() {
tryStatement();
} else if (check(TOKEN_WITH)) {
withStatement();
} else if (match(TOKEN_DEL)) {
do {
inDel = 1;
expression();
} while (match(TOKEN_COMMA));
inDel = 0;
/* del already eats linefeeds */
} else {
if (match(TOKEN_RAISE)) {
raiseStatement();
@ -1663,7 +1691,8 @@ static ssize_t resolveUpvalue(Compiler * compiler, KrkToken * name) {
return -1;
}
#define DO_VARIABLE(opset,opget) do { \
#define OP_NONE_LONG -1
#define DO_VARIABLE(opset,opget,opdel) do { \
if (canAssign && match(TOKEN_EQUAL)) { \
expression(); \
EMIT_CONSTANT_OP(opset, arg); \
@ -1671,6 +1700,9 @@ static ssize_t resolveUpvalue(Compiler * compiler, KrkToken * name) {
EMIT_CONSTANT_OP(opget, arg); \
assignmentValue(); \
EMIT_CONSTANT_OP(opset, arg); \
} else if (inDel && matchEndOfDel()) {\
if (opdel == OP_NONE || !canAssign || inDel != 1) { error("Invalid del target"); } else { \
EMIT_CONSTANT_OP(opdel, arg); inDel = 2; } \
} else { \
EMIT_CONSTANT_OP(opget, arg); \
} } while (0)
@ -1678,12 +1710,12 @@ static ssize_t resolveUpvalue(Compiler * compiler, KrkToken * name) {
static void namedVariable(KrkToken name, int canAssign) {
ssize_t arg = resolveLocal(current, &name);
if (arg != -1) {
DO_VARIABLE(OP_SET_LOCAL, OP_GET_LOCAL);
DO_VARIABLE(OP_SET_LOCAL, OP_GET_LOCAL, OP_NONE);
} else if ((arg = resolveUpvalue(current, &name)) != -1) {
DO_VARIABLE(OP_SET_UPVALUE, OP_GET_UPVALUE);
DO_VARIABLE(OP_SET_UPVALUE, OP_GET_UPVALUE, OP_NONE);
} else {
arg = identifierConstant(&name);
DO_VARIABLE(OP_SET_GLOBAL, OP_GET_GLOBAL);
DO_VARIABLE(OP_SET_GLOBAL, OP_GET_GLOBAL, OP_DEL_GLOBAL);
}
}
#undef DO_VARIABLE
@ -1953,6 +1985,7 @@ ParseRule krk_parseRules[] = {
RULE(TOKEN_FALSE, literal, NULL, PREC_NONE),
RULE(TOKEN_FOR, NULL, NULL, PREC_NONE),
RULE(TOKEN_DEF, NULL, NULL, PREC_NONE),
RULE(TOKEN_DEL, NULL, NULL, PREC_NONE),
RULE(TOKEN_IF, NULL, ternary,PREC_TERNARY),
RULE(TOKEN_IN, NULL, in_, PREC_COMPARISON),
RULE(TOKEN_LET, NULL, NULL, PREC_NONE),
@ -2044,6 +2077,9 @@ static void parsePrecedence(Precedence precedence) {
if (canAssign && matchAssignment()) {
error("invalid assignment target");
}
if (inDel == 1 && matchEndOfDel()) {
error("invalid del target");
}
}
static ssize_t identifierConstant(KrkToken * name) {

View File

@ -117,6 +117,7 @@ size_t krk_disassembleInstruction(FILE * f, KrkFunction * func, size_t offset) {
SIMPLE(OP_INVOKE_GETTER)
SIMPLE(OP_INVOKE_SETTER)
SIMPLE(OP_INVOKE_GETSLICE)
SIMPLE(OP_INVOKE_DELETE)
SIMPLE(OP_SWAP)
SIMPLE(OP_FINALIZE)
SIMPLE(OP_IS)
@ -126,9 +127,11 @@ size_t krk_disassembleInstruction(FILE * f, KrkFunction * func, size_t offset) {
CONSTANT(OP_CONSTANT,(void)0)
CONSTANT(OP_GET_GLOBAL,(void)0)
CONSTANT(OP_SET_GLOBAL,(void)0)
CONSTANT(OP_DEL_GLOBAL,(void)0)
CONSTANT(OP_CLASS,(void)0)
CONSTANT(OP_GET_PROPERTY, (void)0)
CONSTANT(OP_SET_PROPERTY, (void)0)
CONSTANT(OP_DEL_PROPERTY,(void)0)
CONSTANT(OP_METHOD, (void)0)
CONSTANT(OP_CLOSURE, CLOSURE_MORE)
CONSTANT(OP_IMPORT, (void)0)

View File

@ -134,6 +134,7 @@ typedef struct KrkClass {
KrkObj * _len;
KrkObj * _enter;
KrkObj * _exit;
KrkObj * _delitem;
} KrkClass;
typedef struct KrkInstance {

View File

@ -541,7 +541,7 @@ void paint_krk_string(struct syntax_state * state, int type) {
}
char * syn_krk_keywords[] = {
"and","class","def","else","for","if","in","import",
"and","class","def","else","for","if","in","import","del",
"let","not","or","return","while","try","except","raise",
"continue","break","as","from","elif","lambda","with","is",
NULL

View File

@ -203,7 +203,12 @@ static KrkTokenType identifierType() {
case 'l': return checkKeyword(2, "ass", TOKEN_CLASS);
case 'o': return checkKeyword(2, "ntinue", TOKEN_CONTINUE);
} break;
case 'd': return checkKeyword(1, "ef", TOKEN_DEF);
case 'd': if (MORE(1)) switch(scanner.start[1]) {
case 'e': if (MORE(2)) switch (scanner.start[2]) {
case 'f': return checkKeyword(3, "", TOKEN_DEF);
case 'l': return checkKeyword(3, "", TOKEN_DEL);
} break;
} break;
case 'e': if (MORE(1)) switch(scanner.start[1]) {
case 'l': if (MORE(2)) switch(scanner.start[2]) {
case 's': return checkKeyword(3,"e", TOKEN_ELSE);

View File

@ -42,6 +42,7 @@ typedef enum {
TOKEN_AND,
TOKEN_CLASS,
TOKEN_DEF,
TOKEN_DEL,
TOKEN_ELSE,
TOKEN_FALSE,
TOKEN_FOR,

49
test/testDel.krk Normal file
View File

@ -0,0 +1,49 @@
let foo, bar, baz
del foo
print("foo" in globals()) # False
print("bar" in globals()) # True
print("baz" in globals()) # True
del bar, baz
print("foo" in globals()) # False
print("bar" in globals()) # False
print("baz" in globals()) # False
let l = [1,2,3,4,5]
del l[1]
print(l) # [1, 3, 4, 5]
del l[3]
print(l) # [1, 3, 4]
try:
del l[3]
except:
print(exception.arg) # List index out of range
let o = object()
o.foo = object()
o.foo.bar = object()
o.foo.bar.baz = 42
o.foo.bar.qux = "hi"
print(dir(o.foo.bar))
print(o.foo.bar.baz)
del o.foo.bar.baz
print(dir(o.foo.bar))
print(o.foo.bar.qux)
try:
print(o.foo.bar.baz)
except:
print(exception.arg) # AttributeError
del o.foo.bar
print(dir(o.foo))
try:
print(o.foo.bar)
except:
print(exception.arg) # AttributeError
del o.foo
del o

16
test/testDel.krk.expect Normal file
View File

@ -0,0 +1,16 @@
False
True
True
False
False
False
[1, 3, 4, 5]
[1, 3, 4]
list index out of range: 3
['__class__', '__str__', '__dir__', '__repr__', 'baz', 'qux']
42
['__class__', '__str__', '__dir__', '__repr__', 'qux']
hi
'object' object has no attribute 'baz'
['__class__', '__str__', '__dir__', '__repr__']
'object' object has no attribute 'bar'

79
vm.c
View File

@ -301,6 +301,7 @@ void krk_finalizeClass(KrkClass * _class) {
{&_class->_len, METHOD_LEN},
{&_class->_enter, METHOD_ENTER},
{&_class->_exit, METHOD_EXIT},
{&_class->_delitem, METHOD_DELITEM},
{NULL, 0},
};
@ -357,6 +358,30 @@ static KrkValue _dict_set(int argc, KrkValue argv[]) {
return NONE_VAL();
}
/**
* dict.__delitem__
*/
static KrkValue _dict_delitem(int argc, KrkValue argv[]) {
if (argc < 2) {
krk_runtimeError(vm.exceptions.argumentError, "wrong number of arguments");
return NONE_VAL();
}
KrkValue _dict_internal = OBJECT_VAL(AS_INSTANCE(argv[0])->_internal);
if (!krk_tableDelete(AS_DICT(_dict_internal), argv[1])) {
KrkClass * type = AS_CLASS(krk_typeOf(1,&argv[1]));
if (type->_reprer) {
krk_push(argv[1]);
KrkValue asString = krk_callSimple(OBJECT_VAL(type->_reprer), 1, 0);
if (IS_STRING(asString)) {
krk_runtimeError(vm.exceptions.keyError, "%s", AS_CSTRING(asString));
return NONE_VAL();
}
}
krk_runtimeError(vm.exceptions.keyError, "(Unrepresentable value)");
}
return NONE_VAL();
}
/**
* dict.__len__()
*/
@ -3067,6 +3092,7 @@ void krk_initVM(int flags) {
vm.specialMethodNames[METHOD_EQ] = OBJECT_VAL(S("__eq__"));
vm.specialMethodNames[METHOD_ENTER] = OBJECT_VAL(S("__enter__"));
vm.specialMethodNames[METHOD_EXIT] = OBJECT_VAL(S("__exit__"));
vm.specialMethodNames[METHOD_DELITEM] = OBJECT_VAL(S("__delitem__")); /* delitem */
/* Create built-in class `object` */
vm.objectClass = krk_newClass(S("object"));
@ -3301,6 +3327,7 @@ void krk_initVM(int flags) {
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);
@ -3315,6 +3342,7 @@ void krk_initVM(int flags) {
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);
@ -3674,6 +3702,26 @@ static int valueGetProperty(KrkString * name) {
return 0;
}
static int valueDelProperty(KrkString * name) {
if (IS_INSTANCE(krk_peek(0))) {
KrkInstance* instance = AS_INSTANCE(krk_peek(0));
if (!krk_tableDelete(&instance->fields, OBJECT_VAL(name))) {
return 0;
}
krk_pop(); /* the original value */
return 1;
} else if (IS_CLASS(krk_peek(0))) {
KrkClass * _class = AS_CLASS(krk_peek(0));
if (!krk_tableDelete(&_class->fields, OBJECT_VAL(name))) {
return 0;
}
krk_pop(); /* the original value */
return 1;
}
/* TODO del on values? */
return 0;
}
#define READ_BYTE() (*frame->ip++)
#define BINARY_OP(op) { KrkValue b = krk_pop(); KrkValue a = krk_pop(); krk_push(operator_ ## op (a,b)); break; }
#define BINARY_OP_CHECK_ZERO(op) { KrkValue b = krk_pop(); KrkValue a = krk_pop(); \
@ -3859,6 +3907,15 @@ static KrkValue run() {
}
break;
}
case OP_DEL_GLOBAL_LONG:
case OP_DEL_GLOBAL: {
KrkString * name = READ_STRING(operandWidth);
if (!krk_tableDelete(frame->globals, OBJECT_VAL(name))) {
krk_runtimeError(vm.exceptions.nameError, "Undefined variable '%s'.", name->chars);
goto _finishException;
}
break;
}
case OP_IMPORT_LONG:
case OP_IMPORT: {
KrkString * name = READ_STRING(operandWidth);
@ -3995,6 +4052,15 @@ static KrkValue run() {
}
break;
}
case OP_DEL_PROPERTY_LONG:
case OP_DEL_PROPERTY: {
KrkString * name = READ_STRING(operandWidth);
if (!valueDelProperty(name)) {
krk_runtimeError(vm.exceptions.attributeError, "'%s' object has no attribute '%s'", krk_typeName(krk_peek(0)), name->chars);
goto _finishException;
}
break;
}
case OP_INVOKE_GETTER: {
KrkClass * type = AS_CLASS(krk_typeOf(1,(KrkValue[]){krk_peek(1)}));
if (type->_getter) {
@ -4026,6 +4092,19 @@ static KrkValue run() {
}
break;
}
case OP_INVOKE_DELETE: {
KrkClass * type = AS_CLASS(krk_typeOf(1,(KrkValue[]){krk_peek(1)}));
if (type->_delitem) {
krk_callSimple(OBJECT_VAL(type->_delitem), 2, 0);
} else {
if (type->_getter) {
krk_runtimeError(vm.exceptions.attributeError, "'%s' object is not mutable", krk_typeName(krk_peek(1)));
} else {
krk_runtimeError(vm.exceptions.attributeError, "'%s' object is not subscriptable", krk_typeName(krk_peek(1)));
}
}
break;
}
case OP_SET_PROPERTY_LONG:
case OP_SET_PROPERTY: {
KrkString * name = READ_STRING(operandWidth);

1
vm.h
View File

@ -41,6 +41,7 @@ typedef enum {
METHOD_EQ,
METHOD_ENTER,
METHOD_EXIT,
METHOD_DELITEM,
METHOD__MAX,
} KrkSpecialMethods;