✨ array arithmetic.
parent
f1b82208ce
commit
462959e6ab
|
@ -1,9 +1,8 @@
|
|||
MODULE ::= EXPR*
|
||||
EXPR ::=
|
||||
| IN
|
||||
| OR
|
||||
| ASSERT
|
||||
ASSERT ::= (assert_eq|assert_ne) tuple
|
||||
IN ::= OR (in OR)?
|
||||
OR ::= AND (or AND)*
|
||||
AND ::= EQNE (and EQNE)*
|
||||
EQNE ::= CMP ((eq|ne) CMP)?
|
||||
|
@ -12,14 +11,17 @@ TERM ::= FACTOR ((add|sub) FACTOR)*
|
|||
FACTOR ::= USUB ((mul|div|mod) USUB)*
|
||||
USUB ::= sub* NOT
|
||||
NOT ::= not* POW
|
||||
POW ::= LITERAL (pow LITERAL)?
|
||||
POW ::= IN (pow IN)?
|
||||
IN ::= LITERAL (in LITERAL)?
|
||||
LITERAL ::=
|
||||
| BUILTIN
|
||||
| TUPLE
|
||||
| INDEX
|
||||
| ARRAY
|
||||
| opar EXPR cpar
|
||||
ARRAY ::= osquare (EXPR (comma EXPR)*)? csquare
|
||||
INDEX ::=
|
||||
| (TUPLE|str) osquare (EXPR (comma EXPR)*)? csquare
|
||||
| (TUPLE|str|ARRAY) osquare (EXPR (comma EXPR)*)? csquare
|
||||
TUPLE ::=
|
||||
| opar EXPR+ cpar
|
||||
BUILTIN ::= num | bool | str
|
||||
|
|
|
@ -9,7 +9,7 @@ G(OP_ADD), G(OP_SUB), G(OP_USUB), G(OP_MUL), \
|
|||
G(OP_DIV), G(OP_POW), G(OP_MOD), G(OP_MK_TUPLE), \
|
||||
G(OP_ASSERT_EQ), G(OP_ASSERT_NE), G(OP_BRF), G(OP_BR), \
|
||||
G(OP_NOT), G(OP_IN), G(OP_INDEX), G(OP_EQ), G(OP_LT), \
|
||||
G(OP_GT)
|
||||
G(OP_GT), G(OP_MK_ARRAY)
|
||||
|
||||
CCM_ENUM_H(Opcode, OPCODES);
|
||||
|
||||
|
|
301
lib/ccm.c
301
lib/ccm.c
|
@ -6,16 +6,25 @@ void ccm_init(ccm_t* self)
|
|||
assert(self);
|
||||
vec_init(&self->values);
|
||||
vec_init(&self->stack);
|
||||
vec_init(&self->globals);
|
||||
err_init(&self->err);
|
||||
self->id_counter = 0;
|
||||
}
|
||||
|
||||
void ccm_entry_free(ccm_entry_t* self)
|
||||
{
|
||||
value_free(self->value);
|
||||
free(self->value);
|
||||
}
|
||||
|
||||
void ccm_free(ccm_t* self)
|
||||
{
|
||||
assert(self);
|
||||
err_free(&self->err);
|
||||
vec_free_elements(&self->values, (void*) value_free);
|
||||
vec_free_elements(&self->values, (void*) ccm_entry_free);
|
||||
vec_free(&self->values);
|
||||
vec_free(&self->stack);
|
||||
vec_free(&self->globals);
|
||||
}
|
||||
|
||||
size_t ccm_str(ccm_t* self, char* buffer, size_t size)
|
||||
|
@ -27,7 +36,7 @@ size_t ccm_str(ccm_t* self, char* buffer, size_t size)
|
|||
|
||||
for (size_t i=0; i<self->stack.size; i++)
|
||||
{
|
||||
value_t* val = self->values.data[(CCM) self->stack.data[i]];
|
||||
value_t* val = ccm_to_value(self, (CCM) self->stack.data[i]);
|
||||
sz += value_str(val, buffer + sz, size - sz);
|
||||
sz += snprintf(buffer + sz, size - sz, "\n");
|
||||
}
|
||||
|
@ -35,6 +44,48 @@ size_t ccm_str(ccm_t* self, char* buffer, size_t size)
|
|||
return sz;
|
||||
}
|
||||
|
||||
value_t* ccm_find_value(ccm_t* self, size_t addr)
|
||||
{
|
||||
for (size_t i=0; i<self->values.size; i++)
|
||||
{
|
||||
if (((ccm_entry_t*) self->values.data[i])->id == (int) addr)
|
||||
{
|
||||
return ((ccm_entry_t*)self->values.data[i])->value;
|
||||
}
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
|
||||
size_t ccm_add_value(ccm_t* self, value_t* value)
|
||||
{
|
||||
assert(self);
|
||||
assert(value);
|
||||
|
||||
ccm_entry_t* entry = malloc(sizeof(ccm_entry_t));
|
||||
entry->value = value;
|
||||
entry->id = self->id_counter;
|
||||
self->id_counter++;
|
||||
|
||||
vec_push(&self->values, entry);
|
||||
return self->id_counter - 1;
|
||||
}
|
||||
|
||||
size_t ccm_store_global(ccm_t* self, CCM value)
|
||||
{
|
||||
assert(self);
|
||||
|
||||
vec_push(&self->globals, (void*) value);
|
||||
return self->globals.size - 1;
|
||||
}
|
||||
|
||||
CCM ccm_load_global(ccm_t* self, size_t addr)
|
||||
{
|
||||
assert(self);
|
||||
assert(addr < self->globals.size);
|
||||
|
||||
return (CCM) self->globals.data[addr];
|
||||
}
|
||||
|
||||
CCM ccm_from_value(ccm_t* self, value_t* value)
|
||||
{
|
||||
assert(self);
|
||||
|
@ -58,6 +109,22 @@ CCM ccm_from_value(ccm_t* self, value_t* value)
|
|||
);
|
||||
} break;
|
||||
|
||||
case TYPE_ARRAY: {
|
||||
vec_t * vec = malloc(sizeof(vec_t));
|
||||
vec_init(vec);
|
||||
for (size_t i=0; i<value->data.array->size; i++)
|
||||
{
|
||||
vec_push(vec,
|
||||
value->data.array->data[i]
|
||||
);
|
||||
}
|
||||
|
||||
return ccm_to_array(
|
||||
self,
|
||||
vec,
|
||||
value->line
|
||||
);
|
||||
} break;
|
||||
case TYPE_TUPLE: {
|
||||
vec_t * vec = malloc(sizeof(vec_t));
|
||||
vec_init(vec);
|
||||
|
@ -87,13 +154,19 @@ CCM ccm_from_value(ccm_t* self, value_t* value)
|
|||
value_t* ccm_to_value(ccm_t* self, CCM value)
|
||||
{
|
||||
assert(self);
|
||||
assert(value < self->values.size);
|
||||
return (value_t*) self->values.data[value];
|
||||
value_t* val = ccm_find_value(self, value);
|
||||
|
||||
if (val->type == TYPE_REF)
|
||||
{
|
||||
CCM addr = (CCM) self->globals.data[val->data.ref];
|
||||
return ccm_to_value(self, addr);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
int ccm_is_num(ccm_t* self, CCM value)
|
||||
{
|
||||
value_t const* val = self->values.data[value];
|
||||
value_t const* val = ccm_find_value(self, value);
|
||||
return val->type == TYPE_NUM;
|
||||
}
|
||||
|
||||
|
@ -102,16 +175,14 @@ double ccm_from_num(ccm_t* self, CCM value)
|
|||
assert(self);
|
||||
assert(value < self->values.size);
|
||||
|
||||
value_t* val = ccm_find_value(self, value);
|
||||
|
||||
if (!ccm_is_num(self, value))
|
||||
{
|
||||
err_push(&self->err,
|
||||
((value_t*) self->values.data[value])->line,
|
||||
"not a num");
|
||||
err_push(&self->err, val->line, "not a num");
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
value_t* val = self->values.data[value];
|
||||
|
||||
return val->data.num;
|
||||
}
|
||||
|
||||
|
@ -120,15 +191,14 @@ CCM ccm_to_num(ccm_t* self, double value, int line)
|
|||
assert(self);
|
||||
value_t* val = malloc(sizeof(value_t));
|
||||
value_init_num(val, value, line);
|
||||
vec_push(&self->values, val);
|
||||
|
||||
return self->values.size - 1;
|
||||
return ccm_add_value(self, val);
|
||||
}
|
||||
|
||||
int ccm_is_tuple(ccm_t* self, CCM value)
|
||||
{
|
||||
assert(self);
|
||||
value_t* val = self->values.data[value];
|
||||
value_t* val = ccm_find_value(self, value);
|
||||
assert(val);
|
||||
return val->type == TYPE_TUPLE;
|
||||
}
|
||||
|
@ -136,7 +206,8 @@ int ccm_is_tuple(ccm_t* self, CCM value)
|
|||
vec_t* ccm_from_tuple(ccm_t* self, CCM value)
|
||||
{
|
||||
assert(self);
|
||||
value_t* val = self->values.data[value];
|
||||
value_t* val = ccm_find_value(self, value);
|
||||
|
||||
if (!ccm_is_tuple(self, value))
|
||||
{
|
||||
err_push(&self->err, val->line, "not a tuple");
|
||||
|
@ -152,41 +223,38 @@ CCM ccm_to_tuple(ccm_t* self, vec_t* values, int line)
|
|||
value_t* value = malloc(sizeof(value_t));
|
||||
value_init_new_tuple(value, values, line);
|
||||
|
||||
vec_push(&self->values, value);
|
||||
return self->values.size - 1;
|
||||
return ccm_add_value(self, value);
|
||||
}
|
||||
|
||||
int ccm_is_boolean(ccm_t* self, CCM value)
|
||||
{
|
||||
assert(self);
|
||||
return ((value_t*) self->values.data[value])->type
|
||||
== TYPE_BOOLEAN;
|
||||
return ccm_find_value(self, value)->type == TYPE_BOOLEAN;
|
||||
}
|
||||
|
||||
int ccm_from_boolean(ccm_t* self, CCM value)
|
||||
{
|
||||
assert(self);
|
||||
return ((value_t*) self->values.data[value])->data.boolean;
|
||||
return ccm_find_value(self, value)->data.boolean;
|
||||
}
|
||||
|
||||
CCM ccm_to_boolean(ccm_t* self, int value, int line)
|
||||
{
|
||||
value_t* boolean = malloc(sizeof(value_t));
|
||||
value_init_boolean(boolean, value, line);
|
||||
vec_push(&self->values, boolean);
|
||||
return self->values.size - 1;
|
||||
return ccm_add_value(self, boolean);
|
||||
}
|
||||
|
||||
int ccm_is_str(ccm_t* self, CCM value)
|
||||
{
|
||||
assert(self);
|
||||
return ((value_t*) self->values.data[value])->type == TYPE_STR;
|
||||
return ccm_find_value(self, value)->type == TYPE_STR;
|
||||
}
|
||||
|
||||
char* ccm_from_str(ccm_t* self, CCM value)
|
||||
{
|
||||
assert(self);
|
||||
return ((value_t*) self->values.data[value])->data.str;
|
||||
return ccm_find_value(self, value)->data.str;
|
||||
}
|
||||
|
||||
CCM ccm_to_str(ccm_t* self, char* value, int line)
|
||||
|
@ -197,9 +265,50 @@ CCM ccm_to_str(ccm_t* self, char* value, int line)
|
|||
value_t* val = malloc(sizeof(value_t));
|
||||
value_init_str(val, value, line);
|
||||
|
||||
vec_push(&self->values, val);
|
||||
return ccm_add_value(self, val);
|
||||
}
|
||||
|
||||
return self->values.size - 1;
|
||||
int ccm_is_array(ccm_t* self, CCM value)
|
||||
{
|
||||
assert(self);
|
||||
value_t const* val = ccm_find_value(self, value);
|
||||
return val->type == TYPE_ARRAY;
|
||||
}
|
||||
|
||||
vec_t* ccm_from_array(ccm_t* self, CCM value)
|
||||
{
|
||||
assert(self);
|
||||
value_t* val = ccm_find_value(self, value);
|
||||
return val->data.array;
|
||||
}
|
||||
|
||||
CCM ccm_to_array(ccm_t* self, vec_t* value, int line)
|
||||
{
|
||||
assert(self);
|
||||
value_t* val = malloc(sizeof(value_t));
|
||||
value_init_new_array(val, value, line);
|
||||
return ccm_add_value(self, val);
|
||||
}
|
||||
|
||||
int ccm_is_ref(ccm_t* self, CCM value)
|
||||
{
|
||||
assert(self);
|
||||
return ccm_find_value(self, value)->type == TYPE_REF;
|
||||
}
|
||||
|
||||
size_t ccm_from_ref(ccm_t* self, CCM value)
|
||||
{
|
||||
assert(self);
|
||||
return ccm_find_value(self, value)->data.ref;
|
||||
}
|
||||
|
||||
CCM ccm_to_ref(ccm_t* self, size_t value, int line)
|
||||
{
|
||||
assert(self);
|
||||
|
||||
value_t* val = malloc(sizeof(value_t));
|
||||
value_init_ref(val, value, line);
|
||||
return ccm_add_value(self, val);
|
||||
}
|
||||
|
||||
void ccm_push(ccm_t* self, CCM value)
|
||||
|
@ -229,17 +338,13 @@ void ccm_in(ccm_t* self)
|
|||
CCM ccm_rhs = ccm_pop(self);
|
||||
CCM ccm_lhs = ccm_pop(self);
|
||||
|
||||
int line = ((value_t*) self->values.data[ccm_lhs])->line;
|
||||
int line = ccm_find_value(self, ccm_lhs)->line;
|
||||
|
||||
if (!ccm_is_tuple(self, ccm_rhs))
|
||||
{
|
||||
err_push(&self->err, line, "cannot test membership");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
vec_t* rhs = ccm_from_tuple(self, ccm_rhs);
|
||||
value_t* val = ((value_t*)self->values.data[ccm_lhs]);
|
||||
if (ccm_is_ref(self, ccm_rhs)) {
|
||||
value_t* rhs_val = ccm_to_value(self, ccm_rhs);
|
||||
value_t* val = ccm_to_value(self, ccm_lhs);
|
||||
assert(rhs_val->type == TYPE_ARRAY);
|
||||
vec_t* rhs = rhs_val->data.array;
|
||||
|
||||
for (size_t i=0; i<rhs->size; i++)
|
||||
{
|
||||
|
@ -251,6 +356,26 @@ void ccm_in(ccm_t* self)
|
|||
}
|
||||
|
||||
ccm_push(self, ccm_to_boolean(self, 0, line));
|
||||
return;
|
||||
}
|
||||
if (ccm_is_tuple(self, ccm_rhs)) {
|
||||
vec_t* rhs = ccm_from_tuple(self, ccm_rhs);
|
||||
value_t* val = ccm_find_value(self, ccm_lhs);
|
||||
|
||||
for (size_t i=0; i<rhs->size; i++)
|
||||
{
|
||||
if (value_equals(val, rhs->data[i]))
|
||||
{
|
||||
ccm_push(self, ccm_to_boolean(self, 1, line));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ccm_push(self, ccm_to_boolean(self, 0, line));
|
||||
return;
|
||||
}
|
||||
err_push(&self->err, line, "cannot test membership");
|
||||
return;
|
||||
}
|
||||
|
||||
void ccm_add(ccm_t* self)
|
||||
|
@ -258,8 +383,44 @@ void ccm_add(ccm_t* self)
|
|||
assert(self);
|
||||
CCM ccm_rhs = ccm_pop(self);
|
||||
CCM ccm_lhs = ccm_pop(self);
|
||||
int line = ((value_t*) self->values.data[ccm_lhs])->line;
|
||||
int line = ccm_find_value(self, ccm_lhs)->line;
|
||||
|
||||
if (ccm_is_ref(self, ccm_rhs)
|
||||
&& ccm_is_ref(self, ccm_lhs))
|
||||
{
|
||||
value_t* lhs = ccm_to_value(self, ccm_lhs);
|
||||
value_t* rhs = ccm_to_value(self, ccm_rhs);
|
||||
|
||||
vec_t* data = malloc(sizeof(vec_t));
|
||||
vec_init(data);
|
||||
|
||||
for (size_t i=0; i<lhs->data.array->size; i++)
|
||||
{
|
||||
vec_push(data,
|
||||
lhs->data.array->data[i]);
|
||||
}
|
||||
|
||||
for (size_t i=0; i<rhs->data.array->size; i++)
|
||||
{
|
||||
vec_push(data,
|
||||
rhs->data.array->data[i]);
|
||||
}
|
||||
|
||||
value_t* res = malloc(sizeof(value_t));
|
||||
value_init_new_array(res, data, lhs->line);
|
||||
|
||||
CCM res_id = ccm_store_global(
|
||||
self,
|
||||
ccm_add_value(self, res)
|
||||
);
|
||||
|
||||
value_t* ref = malloc(sizeof(value_t));
|
||||
value_init_ref(ref, res_id, lhs->line);
|
||||
|
||||
ccm_push(self, ccm_add_value(self, ref));
|
||||
|
||||
return;
|
||||
}
|
||||
if (ccm_is_str(self, ccm_rhs)
|
||||
&& ccm_is_str(self, ccm_lhs))
|
||||
{
|
||||
|
@ -286,8 +447,7 @@ void ccm_sub(ccm_t* self)
|
|||
assert(self);
|
||||
CCM ccm_rhs = ccm_pop(self);
|
||||
CCM ccm_lhs = ccm_pop(self);
|
||||
int line = ((value_t*) self->values.data[ccm_lhs])->line;
|
||||
|
||||
int line = ccm_find_value(self, ccm_lhs)->line;
|
||||
double rhs = ccm_from_num(self, ccm_rhs);
|
||||
double lhs = ccm_from_num(self, ccm_lhs);
|
||||
|
||||
|
@ -298,7 +458,7 @@ void ccm_usub(ccm_t* self)
|
|||
{
|
||||
assert(self);
|
||||
CCM ccm_lhs = ccm_pop(self);
|
||||
int line = ((value_t*) self->values.data[ccm_lhs])->line;
|
||||
int line = ccm_find_value(self, ccm_lhs)->line;
|
||||
|
||||
double lhs = ccm_from_num(self, ccm_lhs);
|
||||
|
||||
|
@ -310,8 +470,48 @@ void ccm_mul(ccm_t* self)
|
|||
assert(self);
|
||||
CCM ccm_rhs = ccm_pop(self);
|
||||
CCM ccm_lhs = ccm_pop(self);
|
||||
int line = ((value_t*) self->values.data[ccm_lhs])->line;
|
||||
|
||||
int line = ccm_find_value(self, ccm_lhs)->line;
|
||||
|
||||
if (ccm_is_ref(self, ccm_rhs)
|
||||
&& ccm_is_num(self, ccm_lhs))
|
||||
{
|
||||
CCM tmp = ccm_lhs;
|
||||
ccm_lhs = ccm_rhs;
|
||||
ccm_rhs = tmp;
|
||||
}
|
||||
|
||||
if (ccm_is_ref(self, ccm_lhs)
|
||||
&& ccm_is_num(self, ccm_rhs))
|
||||
{
|
||||
int count = ccm_from_num(self, ccm_rhs);
|
||||
value_t* val = ccm_to_value(self, ccm_lhs);
|
||||
vec_t* vec = malloc(sizeof(vec_t));
|
||||
vec_init(vec);
|
||||
|
||||
for (int i=0; i<count; i++)
|
||||
{
|
||||
for (size_t j=0; j<val->data.array->size; j++)
|
||||
{
|
||||
vec_push(
|
||||
vec,
|
||||
val->data.array->data[j]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
value_t* result = malloc(sizeof(value_t));
|
||||
value_init_new_array(result, vec, val->line);
|
||||
|
||||
int id = ccm_store_global(
|
||||
self,
|
||||
ccm_add_value(self, result)
|
||||
);
|
||||
CCM res = ccm_to_ref(self, id, val->line);
|
||||
ccm_push(self, res);
|
||||
|
||||
return;
|
||||
}
|
||||
if (ccm_is_str(self, ccm_rhs)
|
||||
&& ccm_is_num(self, ccm_lhs))
|
||||
{
|
||||
|
@ -349,8 +549,7 @@ void ccm_div(ccm_t* self)
|
|||
assert(self);
|
||||
CCM ccm_rhs = ccm_pop(self);
|
||||
CCM ccm_lhs = ccm_pop(self);
|
||||
int line = ((value_t*) self->values.data[ccm_lhs])->line;
|
||||
|
||||
int line = ccm_find_value(self, ccm_lhs)->line;
|
||||
double rhs = ccm_from_num(self, ccm_rhs);
|
||||
double lhs = ccm_from_num(self, ccm_lhs);
|
||||
|
||||
|
@ -362,7 +561,7 @@ void ccm_mod(ccm_t* self)
|
|||
assert(self);
|
||||
CCM ccm_rhs = ccm_pop(self);
|
||||
CCM ccm_lhs = ccm_pop(self);
|
||||
int line = ((value_t*) self->values.data[ccm_lhs])->line;
|
||||
int line = ccm_find_value(self, ccm_lhs)->line;
|
||||
|
||||
double rhs = ccm_from_num(self, ccm_rhs);
|
||||
double lhs = ccm_from_num(self, ccm_lhs);
|
||||
|
@ -375,7 +574,7 @@ void ccm_pow(ccm_t* self)
|
|||
assert(self);
|
||||
CCM ccm_rhs = ccm_pop(self);
|
||||
CCM ccm_lhs = ccm_pop(self);
|
||||
int line = ((value_t*) self->values.data[ccm_lhs])->line;
|
||||
int line = ccm_find_value(self, ccm_lhs)->line;
|
||||
|
||||
double rhs = ccm_from_num(self, ccm_rhs);
|
||||
double lhs = ccm_from_num(self, ccm_lhs);
|
||||
|
@ -387,8 +586,8 @@ void ccm_not(ccm_t* self)
|
|||
{
|
||||
assert(self);
|
||||
CCM ccm_lhs = ccm_pop(self);
|
||||
int line = ((value_t*) self->values.data[ccm_lhs])->line;
|
||||
|
||||
int line = ccm_find_value(self, ccm_lhs)->line;
|
||||
int lhs = ccm_from_boolean(self, ccm_lhs);
|
||||
|
||||
ccm_push(self, ccm_to_boolean(self, !lhs, line));
|
||||
|
@ -398,11 +597,9 @@ void ccm_eq(ccm_t* self)
|
|||
{
|
||||
CCM ccm_rhs = ccm_pop(self);
|
||||
CCM ccm_lhs = ccm_pop(self);
|
||||
|
||||
value_t* lhs = ccm_to_value(self, ccm_lhs);
|
||||
value_t* rhs = ccm_to_value(self, ccm_rhs);
|
||||
int line = ((value_t*) self->values.data[ccm_lhs])->line;
|
||||
|
||||
int line = ccm_find_value(self, ccm_lhs)->line;
|
||||
int eq = value_equals(lhs, rhs);
|
||||
ccm_push(self, ccm_to_boolean(self, eq, line));
|
||||
}
|
||||
|
@ -414,8 +611,7 @@ void ccm_ne(ccm_t* self)
|
|||
|
||||
value_t* lhs = ccm_to_value(self, ccm_lhs);
|
||||
value_t* rhs = ccm_to_value(self, ccm_rhs);
|
||||
int line = ((value_t*) self->values.data[ccm_lhs])->line;
|
||||
|
||||
int line = ccm_find_value(self, ccm_lhs)->line;
|
||||
int eq = value_equals(lhs, rhs);
|
||||
ccm_push(self, ccm_to_boolean(self, !eq, line));
|
||||
}
|
||||
|
@ -427,8 +623,8 @@ void ccm_lt(ccm_t* self)
|
|||
|
||||
double lhs = ccm_from_num(self, ccm_lhs);
|
||||
double rhs = ccm_from_num(self, ccm_rhs);
|
||||
int line = ((value_t*) self->values.data[ccm_lhs])->line;
|
||||
|
||||
int line = ccm_find_value(self, ccm_lhs)->line;
|
||||
ccm_push(self, ccm_to_boolean(self, lhs < rhs, line));
|
||||
}
|
||||
|
||||
|
@ -445,8 +641,7 @@ void ccm_gt(ccm_t* self)
|
|||
|
||||
double lhs = ccm_from_num(self, ccm_lhs);
|
||||
double rhs = ccm_from_num(self, ccm_rhs);
|
||||
int line = ((value_t*) self->values.data[ccm_lhs])->line;
|
||||
|
||||
int line = ccm_find_value(self, ccm_lhs)->line;
|
||||
ccm_push(self, ccm_to_boolean(self, lhs > rhs, line));
|
||||
}
|
||||
|
||||
|
|
23
lib/ccm.h
23
lib/ccm.h
|
@ -6,18 +6,31 @@
|
|||
#include "value.h"
|
||||
#include "err.h"
|
||||
|
||||
typedef unsigned long long CCM;
|
||||
typedef size_t CCM;
|
||||
|
||||
typedef struct {
|
||||
int id;
|
||||
value_t* value;
|
||||
} ccm_entry_t;
|
||||
|
||||
typedef struct {
|
||||
err_t err;
|
||||
vec_t values;
|
||||
vec_t stack;
|
||||
vec_t globals;
|
||||
int id_counter;
|
||||
} ccm_t;
|
||||
|
||||
void ccm_init(ccm_t* self);
|
||||
void ccm_entry_free(ccm_entry_t* self);
|
||||
void ccm_free(ccm_t* self);
|
||||
|
||||
size_t ccm_str(ccm_t* self, char* buffer, size_t size);
|
||||
value_t* ccm_find_value(ccm_t* self, size_t addr);
|
||||
size_t ccm_add_value(ccm_t* self, value_t* value);
|
||||
|
||||
size_t ccm_store_global(ccm_t* self, CCM value);
|
||||
CCM ccm_load_global(ccm_t* self, size_t addr);
|
||||
|
||||
CCM ccm_from_value(ccm_t* self, value_t* value);
|
||||
value_t* ccm_to_value(ccm_t* self, CCM value);
|
||||
|
@ -38,6 +51,14 @@ int ccm_is_str(ccm_t* self, CCM value);
|
|||
char* ccm_from_str(ccm_t* self, CCM value);
|
||||
CCM ccm_to_str(ccm_t* self, char* value, int line);
|
||||
|
||||
int ccm_is_array(ccm_t* self, CCM value);
|
||||
vec_t* ccm_from_array(ccm_t* self, CCM value);
|
||||
CCM ccm_to_array(ccm_t* self, vec_t* value, int line);
|
||||
|
||||
int ccm_is_ref(ccm_t* self, CCM value);
|
||||
size_t ccm_from_ref(ccm_t* self, CCM value);
|
||||
CCM ccm_to_ref(ccm_t* self, size_t value, int line);
|
||||
|
||||
void ccm_push(ccm_t* self, CCM value);
|
||||
CCM ccm_pop(ccm_t* self);
|
||||
CCM ccm_top(ccm_t* self, int depth);
|
||||
|
|
|
@ -30,6 +30,15 @@ void compiler_compile(compiler_t* self,
|
|||
|
||||
switch (node->kind)
|
||||
{
|
||||
case NODE_ARRAY: {
|
||||
for (size_t i=0; i<node->children.size; i++)
|
||||
{
|
||||
size_t k = node->children.size - 1 - i;
|
||||
compiler_compile(self, node->children.data[k], prog);
|
||||
}
|
||||
prog_add_instr(prog, OP_MK_ARRAY, node->children.size);
|
||||
} break;
|
||||
|
||||
case NODE_LT: {
|
||||
compiler_compile(self, node->children.data[0], prog);
|
||||
compiler_compile(self, node->children.data[1], prog);
|
||||
|
|
79
lib/exec.c
79
lib/exec.c
|
@ -44,6 +44,27 @@ void exec_instr(exec_t* self,
|
|||
|
||||
switch (op)
|
||||
{
|
||||
case OP_MK_ARRAY: {
|
||||
vec_t* vec = malloc(sizeof(vec_t));
|
||||
vec_init(vec);
|
||||
|
||||
for (int i=0; i<param; i++)
|
||||
{
|
||||
CCM element = ccm_pop(ccm);
|
||||
vec_push(vec, ccm_to_value(ccm, element));
|
||||
}
|
||||
|
||||
int line = ((value_t*) vec->data[0])->line;
|
||||
|
||||
size_t addr = ccm_store_global(
|
||||
ccm,
|
||||
ccm_to_array(ccm, vec, line)
|
||||
);
|
||||
|
||||
ccm_push(ccm, ccm_to_ref(ccm, addr, line));
|
||||
self->pc++;
|
||||
} break;
|
||||
|
||||
case OP_EQ: {
|
||||
ccm_eq(ccm);
|
||||
self->pc++;
|
||||
|
@ -101,9 +122,16 @@ void exec_instr(exec_t* self,
|
|||
char buf[2] = {target[idx], '\0'};
|
||||
ccm_push(ccm, ccm_to_str(ccm, buf, line));
|
||||
}
|
||||
else if (ccm_is_tuple(ccm, ccm_target))
|
||||
else if (ccm_is_tuple(ccm, ccm_target)
|
||||
|| ccm_is_ref(ccm, ccm_target))
|
||||
{
|
||||
vec_t* target = ccm_from_tuple(ccm, ccm_target);
|
||||
vec_t* target;
|
||||
|
||||
if (ccm_is_tuple(ccm, ccm_target)) {
|
||||
target = ccm_from_tuple(ccm, ccm_target);
|
||||
} else {
|
||||
target = ccm_to_value(ccm, ccm_target)->data.array;
|
||||
}
|
||||
|
||||
for (int i=0; i<param; i++)
|
||||
{
|
||||
|
@ -127,11 +155,24 @@ void exec_instr(exec_t* self,
|
|||
|
||||
if (i == param - 1) {
|
||||
result = val;
|
||||
} else {
|
||||
} else if (ccm_is_tuple(ccm, ccm_target) ) {
|
||||
target = val->data.tuple;
|
||||
} else if (ccm_is_ref(ccm, ccm_target)) {
|
||||
target = val->data.array;
|
||||
} else { assert(0); }
|
||||
}
|
||||
|
||||
CCM ccm_result = ccm_from_value(ccm, result);
|
||||
|
||||
if (ccm_is_array(ccm, ccm_result)) {
|
||||
CCM ref = ccm_to_ref(ccm,
|
||||
ccm_store_global(ccm, ccm_result),
|
||||
result->line);
|
||||
ccm_push(ccm, ref);
|
||||
} else {
|
||||
ccm_push(ccm, ccm_result);
|
||||
}
|
||||
ccm_push(ccm, ccm_from_value(ccm, result));
|
||||
|
||||
} else {
|
||||
assert(0);
|
||||
}
|
||||
|
@ -169,6 +210,9 @@ void exec_instr(exec_t* self,
|
|||
|
||||
case OP_ASSERT_NE:
|
||||
case OP_ASSERT_EQ: {
|
||||
// TODO: bug here
|
||||
// [1] == [1] -> OK
|
||||
// assert_eq ([1], [1]) -> Failed
|
||||
CCM val = ccm_pop(ccm);
|
||||
vec_t* values = ccm_from_tuple(ccm, val);
|
||||
assert(values->size == 2);
|
||||
|
@ -179,14 +223,30 @@ void exec_instr(exec_t* self,
|
|||
oracle = 0;
|
||||
}
|
||||
|
||||
if (value_equals(values->data[0],
|
||||
values->data[1]) == !oracle)
|
||||
value_t* val_lhs = values->data[0];
|
||||
value_t* val_rhs = values->data[1];
|
||||
|
||||
if (val_lhs->type == TYPE_REF)
|
||||
{
|
||||
CCM c = ccm_add_value(ccm, val_lhs);
|
||||
value_t* myval = ccm_to_value(ccm, c);
|
||||
val_lhs = myval;
|
||||
}
|
||||
|
||||
if (val_rhs->type == TYPE_REF)
|
||||
{
|
||||
CCM c = ccm_add_value(ccm, val_rhs);
|
||||
value_t* myval = ccm_to_value(ccm, c);
|
||||
val_rhs = myval;
|
||||
}
|
||||
|
||||
if (value_equals(val_lhs, val_rhs) == !oracle)
|
||||
{
|
||||
char lhs[CCM_STRLEN];
|
||||
value_str(values->data[0], lhs, CCM_STRLEN);
|
||||
value_str(val_lhs, lhs, CCM_STRLEN);
|
||||
|
||||
char rhs[CCM_STRLEN];
|
||||
value_str(values->data[1], rhs, CCM_STRLEN);
|
||||
value_str(val_rhs, rhs, CCM_STRLEN);
|
||||
char const* operator = oracle ? "==" : "!=";
|
||||
|
||||
err_push(
|
||||
|
@ -198,7 +258,6 @@ void exec_instr(exec_t* self,
|
|||
}
|
||||
|
||||
self->pc++;
|
||||
|
||||
} break;
|
||||
|
||||
case OP_PUSH: {
|
||||
|
@ -228,7 +287,7 @@ void exec_instr(exec_t* self,
|
|||
for (int i=0; i<param; i++)
|
||||
{
|
||||
CCM val = ccm_pop(ccm);
|
||||
value_t* v = value_new_clone(ccm->values.data[val]);
|
||||
value_t* v = value_new_clone(ccm_to_value(ccm, val));
|
||||
vec_push(values, v);
|
||||
|
||||
if (line == -1)
|
||||
|
|
|
@ -13,7 +13,7 @@ G(NODE_ASSERT_EQ), G(NODE_ASSERT_NE), G(NODE_BOOL), \
|
|||
G(NODE_AND), G(NODE_OR), G(NODE_NOT), G(NODE_IN), \
|
||||
G(NODE_OSQUARE), G(NODE_CSQUARE), G(NODE_INDEX), \
|
||||
G(NODE_STR), G(NODE_LT), G(NODE_LE), G(NODE_GT), \
|
||||
G(NODE_GE), G(NODE_EQ), G(NODE_NE)
|
||||
G(NODE_GE), G(NODE_EQ), G(NODE_NE), G(NODE_ARRAY)
|
||||
|
||||
CCM_ENUM_H(NodeKind, NODE_KIND);
|
||||
|
||||
|
|
114
lib/parser.c
114
lib/parser.c
|
@ -127,7 +127,7 @@ node_t* parser_try_new_expr(parser_t* self)
|
|||
return CCM_TRY(parser_try_new_assert);
|
||||
}
|
||||
|
||||
return CCM_TRY(parser_try_new_in);
|
||||
return CCM_TRY(parser_try_new_or);
|
||||
}
|
||||
|
||||
node_t* parser_try_new_assert(parser_t* self)
|
||||
|
@ -166,36 +166,6 @@ node_t* parser_try_new_assert(parser_t* self)
|
|||
return node;
|
||||
}
|
||||
|
||||
node_t* parser_try_new_in(parser_t* self)
|
||||
{
|
||||
assert(self);
|
||||
node_t* lhs = CCM_TRY(parser_try_new_or);
|
||||
if (!lhs) { return NULL; }
|
||||
|
||||
if (lexer_peek_kind(self->lexer, NODE_IN, 0))
|
||||
{
|
||||
lexer_consume_next(self->lexer, NODE_IN);
|
||||
node_t* node = malloc(sizeof(node_t));
|
||||
node_init(node, NODE_IN, "", lhs->line);
|
||||
|
||||
node_t* rhs = CCM_TRY(parser_try_new_or);
|
||||
|
||||
if (!rhs)
|
||||
{
|
||||
node_free(lhs); free(lhs);
|
||||
node_free(node); free(node);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node_push_new_child(node, lhs);
|
||||
node_push_new_child(node, rhs);
|
||||
lhs = node;
|
||||
}
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
node_t* parser_try_new_or(parser_t* self)
|
||||
{
|
||||
assert(self);
|
||||
|
@ -441,7 +411,7 @@ node_t* parser_try_new_not(parser_t* self)
|
|||
node_t* parser_try_new_pow(parser_t* self)
|
||||
{
|
||||
assert(self);
|
||||
node_t* lhs = CCM_TRY(parser_try_new_literal);
|
||||
node_t* lhs = CCM_TRY(parser_try_new_in);
|
||||
if (!lhs) { return NULL; }
|
||||
|
||||
if (lexer_peek_kind(self->lexer, NODE_POW, 0))
|
||||
|
@ -453,7 +423,7 @@ node_t* parser_try_new_pow(parser_t* self)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
node_t* rhs = CCM_TRY(parser_try_new_literal);
|
||||
node_t* rhs = CCM_TRY(parser_try_new_in);
|
||||
|
||||
if (!rhs)
|
||||
{
|
||||
|
@ -474,10 +444,52 @@ node_t* parser_try_new_pow(parser_t* self)
|
|||
return lhs;
|
||||
}
|
||||
|
||||
node_t* parser_try_new_in(parser_t* self)
|
||||
{
|
||||
assert(self);
|
||||
node_t* lhs = CCM_TRY(parser_try_new_literal);
|
||||
if (!lhs) { return NULL; }
|
||||
|
||||
if (lexer_peek_kind(self->lexer, NODE_IN, 0))
|
||||
{
|
||||
lexer_consume_next(self->lexer, NODE_IN);
|
||||
node_t* node = malloc(sizeof(node_t));
|
||||
node_init(node, NODE_IN, "", lhs->line);
|
||||
|
||||
node_t* rhs = CCM_TRY(parser_try_new_literal);
|
||||
|
||||
if (!rhs)
|
||||
{
|
||||
node_free(lhs); free(lhs);
|
||||
node_free(node); free(node);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node_push_new_child(node, lhs);
|
||||
node_push_new_child(node, rhs);
|
||||
lhs = node;
|
||||
}
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
node_t* parser_try_new_literal(parser_t* self)
|
||||
{
|
||||
assert(self);
|
||||
|
||||
if (lexer_peek_kind(self->lexer, NODE_OSQUARE, 0))
|
||||
{
|
||||
node_t* array = CCM_TRY(parser_try_new_array);
|
||||
|
||||
if (lexer_peek_kind(self->lexer, NODE_OSQUARE, 0))
|
||||
{
|
||||
return CCM_TRY_LL1(parser_try_new_index, array);
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
if (lexer_peek_kind(self->lexer, NODE_OPAR, 0))
|
||||
{
|
||||
node_t* tuple = CCM_TRY(parser_try_new_tuple);
|
||||
|
@ -524,6 +536,42 @@ node_t* parser_try_new_literal(parser_t* self)
|
|||
return CCM_TRY(parser_try_new_builtin);
|
||||
}
|
||||
|
||||
node_t* parser_try_new_array(parser_t* self)
|
||||
{
|
||||
assert(self);
|
||||
|
||||
node_t* node = malloc(sizeof(node_t));
|
||||
node_init(node, NODE_ARRAY, "", self->lexer->line);
|
||||
|
||||
lexer_consume_next(self->lexer, NODE_OSQUARE);
|
||||
|
||||
int first = 1;
|
||||
|
||||
while (!lexer_peek_kind(self->lexer, NODE_CSQUARE, 0))
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
lexer_consume_next(self->lexer, NODE_COMMA);
|
||||
}
|
||||
|
||||
node_t* expr = CCM_TRY(parser_try_new_expr);
|
||||
|
||||
if (!expr)
|
||||
{
|
||||
node_free(node); free(node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node_push_new_child(node, expr);
|
||||
|
||||
first = 0;
|
||||
}
|
||||
|
||||
lexer_consume_next(self->lexer, NODE_CSQUARE);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
node_t* parser_try_new_index(parser_t* self, node_t* target)
|
||||
{
|
||||
assert(self);
|
||||
|
|
|
@ -26,7 +26,6 @@ int parser_ensure(parser_t* self, node_t* node, NodeKind kind);
|
|||
node_t* parser_try_new_module(parser_t* self);
|
||||
node_t* parser_try_new_expr(parser_t* self);
|
||||
node_t* parser_try_new_assert(parser_t* self);
|
||||
node_t* parser_try_new_in(parser_t* self);
|
||||
node_t* parser_try_new_or(parser_t* self);
|
||||
node_t* parser_try_new_and(parser_t* self);
|
||||
node_t* parser_try_new_eqne(parser_t* self);
|
||||
|
@ -36,7 +35,9 @@ node_t* parser_try_new_factor(parser_t* self);
|
|||
node_t* parser_try_new_usub(parser_t* self);
|
||||
node_t* parser_try_new_not(parser_t* self);
|
||||
node_t* parser_try_new_pow(parser_t* self);
|
||||
node_t* parser_try_new_in(parser_t* self);
|
||||
node_t* parser_try_new_literal(parser_t* self);
|
||||
node_t* parser_try_new_array(parser_t* self);
|
||||
node_t* parser_try_new_index(parser_t* self, node_t* target);
|
||||
node_t* parser_try_new_tuple(parser_t* self);
|
||||
node_t* parser_try_new_builtin(parser_t* self);
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
G(TYPE_NUM), \
|
||||
G(TYPE_TUPLE), \
|
||||
G(TYPE_BOOLEAN), \
|
||||
G(TYPE_STR)
|
||||
G(TYPE_STR), \
|
||||
G(TYPE_ARRAY), \
|
||||
G(TYPE_REF)
|
||||
|
||||
CCM_ENUM_H(Type, TYPES);
|
||||
|
||||
|
|
87
lib/value.c
87
lib/value.c
|
@ -35,6 +35,25 @@ void value_init_str(value_t* self, char const* value, int line)
|
|||
self->line = line;
|
||||
}
|
||||
|
||||
void value_init_new_array(value_t* self, vec_t* value, int line)
|
||||
{
|
||||
assert(self);
|
||||
assert(value);
|
||||
|
||||
self->data.array = value;
|
||||
self->type = TYPE_ARRAY;
|
||||
self->line = line;
|
||||
}
|
||||
|
||||
void value_init_ref(value_t* self, size_t value, int line)
|
||||
{
|
||||
assert(self);
|
||||
|
||||
self->data.ref = value;
|
||||
self->type = TYPE_REF;
|
||||
self->line = line;
|
||||
}
|
||||
|
||||
value_t* value_new_clone(value_t* self)
|
||||
{
|
||||
assert(self);
|
||||
|
@ -43,6 +62,21 @@ value_t* value_new_clone(value_t* self)
|
|||
|
||||
switch (self->type)
|
||||
{
|
||||
case TYPE_REF: {
|
||||
value_init_ref(value, self->data.ref, self->line);
|
||||
} break;
|
||||
case TYPE_ARRAY: {
|
||||
vec_t* array = malloc(sizeof(vec_t));
|
||||
vec_init(array);
|
||||
for (size_t i=0; i<self->data.array->size; i++)
|
||||
{
|
||||
vec_push(array,
|
||||
self->data.array->data[i]
|
||||
);
|
||||
}
|
||||
value_init_new_array(value, array, self->line);
|
||||
} break;
|
||||
|
||||
case TYPE_STR: {
|
||||
value_init_str(value, self->data.str, self->line);
|
||||
} break;
|
||||
|
@ -79,6 +113,13 @@ value_t* value_new_clone(value_t* self)
|
|||
|
||||
void value_free(value_t* self)
|
||||
{
|
||||
if (self->type == TYPE_ARRAY)
|
||||
{
|
||||
//vec_free_elements(self->data.array, (void*) value_free);
|
||||
vec_free(self->data.array);
|
||||
free(self->data.array);
|
||||
}
|
||||
|
||||
if (self->type == TYPE_TUPLE)
|
||||
{
|
||||
vec_free_elements(self->data.tuple, (void*) value_free);
|
||||
|
@ -100,6 +141,29 @@ size_t value_str(value_t* self, char* buffer, size_t size)
|
|||
|
||||
switch (self->type)
|
||||
{
|
||||
case TYPE_ARRAY: {
|
||||
sz += snprintf(buffer + sz, size - sz, "[");
|
||||
|
||||
for (size_t i=0; i<self->data.array->size; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
sz += snprintf(buffer + sz, size - sz, ", ");
|
||||
}
|
||||
|
||||
sz += value_str(
|
||||
self->data.array->data[i],
|
||||
buffer + sz,
|
||||
size - sz
|
||||
);
|
||||
}
|
||||
|
||||
sz += snprintf(buffer + sz, size - sz, "]");
|
||||
} break;
|
||||
case TYPE_REF: {
|
||||
sz += snprintf(buffer + sz, size - sz, "<ref:%zu>",
|
||||
self->data.ref);
|
||||
} break;
|
||||
case TYPE_STR: {
|
||||
sz += snprintf(buffer + sz, size - sz, "%s",
|
||||
self->data.str);
|
||||
|
@ -152,6 +216,29 @@ int value_equals(value_t* self, value_t* rhs)
|
|||
|
||||
switch (self->type)
|
||||
{
|
||||
case TYPE_ARRAY: {
|
||||
if (self->data.array->size != rhs->data.array->size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (size_t i=0; i<self->data.array->size; i++)
|
||||
{
|
||||
value_t* a = self->data.array->data[i];
|
||||
value_t* b = rhs->data.array->data[i];
|
||||
|
||||
if (!value_equals(a, b))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
} break;
|
||||
|
||||
case TYPE_REF: {
|
||||
return self->data.ref == rhs->data.ref;
|
||||
} break;
|
||||
|
||||
case TYPE_STR: {
|
||||
return strcmp(self->data.str, rhs->data.str) == 0;
|
||||
} break;
|
||||
|
|
|
@ -11,6 +11,8 @@ typedef struct {
|
|||
vec_t* tuple;
|
||||
int boolean;
|
||||
char* str;
|
||||
vec_t* array;
|
||||
size_t ref;
|
||||
} data;
|
||||
|
||||
Type type;
|
||||
|
@ -21,6 +23,9 @@ void value_init_num(value_t* self, double num, int line);
|
|||
void value_init_new_tuple(value_t* self, vec_t* values, int line);
|
||||
void value_init_boolean(value_t* self, int boolean, int line);
|
||||
void value_init_str(value_t* self, char const* value, int line);
|
||||
void value_init_new_array(value_t* self, vec_t* value, int line);
|
||||
void value_init_ref(value_t* self, size_t value, int line);
|
||||
|
||||
value_t* value_new_clone(value_t* self);
|
||||
void value_free(value_t* self);
|
||||
|
||||
|
|
16
lib/vec.c
16
lib/vec.c
|
@ -70,3 +70,19 @@ void* vec_pop(vec_t* self)
|
|||
return value;
|
||||
}
|
||||
|
||||
void vec_remove(vec_t* self, size_t idx, void (*free_fun)(void*))
|
||||
{
|
||||
if (free_fun)
|
||||
{
|
||||
free_fun(self->data[idx]);
|
||||
}
|
||||
|
||||
free(self->data[idx]);
|
||||
|
||||
for (ssize_t i=idx; i<((ssize_t)self->size) - 1; i++)
|
||||
{
|
||||
self->data[i] = self->data[i + 1];
|
||||
}
|
||||
|
||||
vec_pop(self);
|
||||
}
|
||||
|
|
|
@ -18,5 +18,6 @@ void vec_free(vec_t* self);
|
|||
|
||||
void vec_push(vec_t* self, void* value);
|
||||
void* vec_pop(vec_t* self);
|
||||
void vec_remove(vec_t* self, size_t idx, void (*free_fun)(void*));
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
# EQUALITY
|
||||
assert_eq (true, [1, 2] == [1, 2])
|
||||
assert_eq (true, [1, 2] <> [1, 3])
|
||||
assert_eq (false, [1, 2] == [7, 2])
|
||||
assert_eq (false, [1, 2] <> [1, 2])
|
||||
|
||||
# INDEX
|
||||
assert_eq (1, [1, 2, 3][0])
|
||||
assert_eq (2, [1, 2, 3][1])
|
||||
assert_eq (3, [1, 2, 3][2])
|
||||
assert_eq (1, [1, 2, 3][-3])
|
||||
assert_eq (2, [1, 2, 3][-2])
|
||||
assert_eq (3, [1, 2, 3][-1])
|
||||
assert_eq (2, [1, [2, 3]][-1, 0])
|
||||
assert_eq (4, [[1, 2], [3, 4]][-1, -1])
|
||||
assert_eq ([3, 4], [[1, 2], [3, 4]][-1])
|
||||
|
||||
# ARITHMETIC
|
||||
assert_eq ([1, 2, 3], [1, 2, 3])
|
||||
assert_eq ([1, 2, 3], [1, 2] + [3])
|
||||
assert_eq ([2, 2, 2], [2] * 3)
|
||||
assert_eq ([2, 2, 2], 3 * [2])
|
||||
assert_eq ([2, 6, 2, 6], 2*([2] + [6]))
|
||||
|
||||
# MEMBERSHIP
|
||||
assert_eq (true, 2 in [1, 2, 3])
|
||||
assert_eq (false, 4 in [1, 2, 3])
|
||||
assert_eq (true, [2, 3] in [1, [2, 3]])
|
||||
assert_eq (false, [2, 4] in [1, [2, 3]])
|
||||
|
||||
|
Loading…
Reference in New Issue