Add 'del' statement.
This commit is contained in:
parent
a9b25d26ee
commit
d2d1c98a1e
6
chunk.h
6
chunk.h
@ -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 {
|
||||
|
46
compiler.c
46
compiler.c
@ -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 ¤t->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) {
|
||||
|
3
debug.c
3
debug.c
@ -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)
|
||||
|
1
object.h
1
object.h
@ -134,6 +134,7 @@ typedef struct KrkClass {
|
||||
KrkObj * _len;
|
||||
KrkObj * _enter;
|
||||
KrkObj * _exit;
|
||||
KrkObj * _delitem;
|
||||
} KrkClass;
|
||||
|
||||
typedef struct KrkInstance {
|
||||
|
2
rline.c
2
rline.c
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
49
test/testDel.krk
Normal 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
16
test/testDel.krk.expect
Normal 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
79
vm.c
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user