Compare commits
No commits in common. "fe0767bfe576fa7f031fff3285282be54b456c98" and "ab34370e390aa628fe74487833d750071f4beef5" have entirely different histories.
fe0767bfe5
...
ab34370e39
|
@ -1,23 +1,16 @@
|
||||||
MODULE ::= EXPR*
|
MODULE ::= EXPR*
|
||||||
EXPR ::=
|
EXPR ::=
|
||||||
| IN
|
| TERM
|
||||||
| ASSERT
|
| ASSERT
|
||||||
ASSERT ::= (assert_eq|assert_ne) tuple
|
ASSERT ::= (assert_eq|assert_ne) tuple
|
||||||
IN ::= OR (in OR)?
|
|
||||||
OR ::= AND (or AND)*
|
|
||||||
AND ::= TERM (and TERM)*
|
|
||||||
TERM ::= FACTOR ((add|sub) FACTOR)*
|
TERM ::= FACTOR ((add|sub) FACTOR)*
|
||||||
FACTOR ::= USUB ((mul|div|mod) USUB)*
|
FACTOR ::= USUB ((mul|div|mod) USUB)*
|
||||||
USUB ::= sub* NOT
|
USUB ::= sub* POW
|
||||||
NOT ::= not* POW
|
|
||||||
POW ::= LITERAL (pow LITERAL)?
|
POW ::= LITERAL (pow LITERAL)?
|
||||||
LITERAL ::=
|
LITERAL ::=
|
||||||
| BUILTIN
|
| BUILTIN
|
||||||
| TUPLE
|
| TUPLE
|
||||||
| INDEX
|
|
||||||
| opar EXPR cpar
|
| opar EXPR cpar
|
||||||
INDEX ::=
|
|
||||||
| TUPLE osquare (EXPR (comma EXPR)*)? csquare
|
|
||||||
TUPLE ::=
|
TUPLE ::=
|
||||||
| opar EXPR+ cpar
|
| opar EXPR+ cpar
|
||||||
BUILTIN ::= num | bool
|
BUILTIN ::= num
|
||||||
|
|
|
@ -7,8 +7,7 @@
|
||||||
G(OP_PUSH), G(OP_POP), \
|
G(OP_PUSH), G(OP_POP), \
|
||||||
G(OP_ADD), G(OP_SUB), G(OP_USUB), G(OP_MUL), \
|
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_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_ASSERT_EQ), G(OP_ASSERT_NE)
|
||||||
G(OP_NOT), G(OP_IN), G(OP_INDEX)
|
|
||||||
|
|
||||||
CCM_ENUM_H(Opcode, OPCODES);
|
CCM_ENUM_H(Opcode, OPCODES);
|
||||||
|
|
||||||
|
|
107
lib/ccm.c
107
lib/ccm.c
|
@ -34,51 +34,6 @@ size_t ccm_str(ccm_t* self, char* buffer, size_t size)
|
||||||
return sz;
|
return sz;
|
||||||
}
|
}
|
||||||
|
|
||||||
CCM ccm_from_value(ccm_t* self, value_t* value)
|
|
||||||
{
|
|
||||||
assert(self);
|
|
||||||
assert(value);
|
|
||||||
|
|
||||||
switch (value->type)
|
|
||||||
{
|
|
||||||
case TYPE_NUM: {
|
|
||||||
return ccm_to_num(self, value->data.num, value->line);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case TYPE_BOOLEAN: {
|
|
||||||
return ccm_to_boolean(
|
|
||||||
self,
|
|
||||||
value->data.boolean,
|
|
||||||
value->line
|
|
||||||
);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case TYPE_TUPLE: {
|
|
||||||
vec_t * vec = malloc(sizeof(vec_t));
|
|
||||||
vec_init(vec);
|
|
||||||
for (size_t i=0; i<value->data.tuple->size; i++)
|
|
||||||
{
|
|
||||||
vec_push(vec, value_new_clone(
|
|
||||||
value->data.tuple->data[i]
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ccm_to_tuple(
|
|
||||||
self,
|
|
||||||
vec,
|
|
||||||
value->line
|
|
||||||
);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
default: {
|
|
||||||
fprintf(stderr,
|
|
||||||
"cannot convert value of type <%s> to CCM\n",
|
|
||||||
TypeStr[value->type]);
|
|
||||||
abort();
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int ccm_is_num(ccm_t* self, CCM value)
|
int ccm_is_num(ccm_t* self, CCM value)
|
||||||
{
|
{
|
||||||
value_t const* val = self->values.data[value];
|
value_t const* val = self->values.data[value];
|
||||||
|
@ -144,27 +99,6 @@ CCM ccm_to_tuple(ccm_t* self, vec_t* values, int line)
|
||||||
return self->values.size - 1;
|
return self->values.size - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ccm_is_boolean(ccm_t* self, CCM value)
|
|
||||||
{
|
|
||||||
assert(self);
|
|
||||||
return ((value_t*) self->values.data[value])->type
|
|
||||||
== TYPE_BOOLEAN;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ccm_from_boolean(ccm_t* self, CCM value)
|
|
||||||
{
|
|
||||||
assert(self);
|
|
||||||
return ((value_t*) self->values.data[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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ccm_push(ccm_t* self, CCM value)
|
void ccm_push(ccm_t* self, CCM value)
|
||||||
{
|
{
|
||||||
assert(self);
|
assert(self);
|
||||||
|
@ -186,36 +120,6 @@ CCM ccm_top(ccm_t* self, int depth)
|
||||||
return (CCM) self->stack.data[self->stack.size - 1 - depth];
|
return (CCM) self->stack.data[self->stack.size - 1 - depth];
|
||||||
}
|
}
|
||||||
|
|
||||||
void ccm_in(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;
|
|
||||||
|
|
||||||
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]);
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ccm_add(ccm_t* self)
|
void ccm_add(ccm_t* self)
|
||||||
{
|
{
|
||||||
assert(self);
|
assert(self);
|
||||||
|
@ -304,14 +208,3 @@ void ccm_pow(ccm_t* self)
|
||||||
|
|
||||||
ccm_push(self, ccm_to_num(self, powf(lhs, rhs), line));
|
ccm_push(self, ccm_to_num(self, powf(lhs, rhs), line));
|
||||||
}
|
}
|
||||||
|
|
||||||
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 lhs = ccm_from_boolean(self, ccm_lhs);
|
|
||||||
|
|
||||||
ccm_push(self, ccm_to_boolean(self, !lhs, line));
|
|
||||||
}
|
|
||||||
|
|
|
@ -19,8 +19,6 @@ void ccm_free(ccm_t* self);
|
||||||
|
|
||||||
size_t ccm_str(ccm_t* self, char* buffer, size_t size);
|
size_t ccm_str(ccm_t* self, char* buffer, size_t size);
|
||||||
|
|
||||||
CCM ccm_from_value(ccm_t* self, value_t* value);
|
|
||||||
|
|
||||||
int ccm_is_num(ccm_t* self, CCM value);
|
int ccm_is_num(ccm_t* self, CCM value);
|
||||||
double ccm_from_num(ccm_t* self, CCM value);
|
double ccm_from_num(ccm_t* self, CCM value);
|
||||||
CCM ccm_to_num(ccm_t* self, double value, int line);
|
CCM ccm_to_num(ccm_t* self, double value, int line);
|
||||||
|
@ -29,15 +27,10 @@ int ccm_is_tuple(ccm_t* self, CCM value);
|
||||||
vec_t* ccm_from_tuple(ccm_t* self, CCM value);
|
vec_t* ccm_from_tuple(ccm_t* self, CCM value);
|
||||||
CCM ccm_to_tuple(ccm_t* self, vec_t* values, int line);
|
CCM ccm_to_tuple(ccm_t* self, vec_t* values, int line);
|
||||||
|
|
||||||
int ccm_is_boolean(ccm_t* self, CCM value);
|
|
||||||
int ccm_from_boolean(ccm_t* self, CCM value);
|
|
||||||
CCM ccm_to_boolean(ccm_t* self, int value, int line);
|
|
||||||
|
|
||||||
void ccm_push(ccm_t* self, CCM value);
|
void ccm_push(ccm_t* self, CCM value);
|
||||||
CCM ccm_pop(ccm_t* self);
|
CCM ccm_pop(ccm_t* self);
|
||||||
CCM ccm_top(ccm_t* self, int depth);
|
CCM ccm_top(ccm_t* self, int depth);
|
||||||
|
|
||||||
void ccm_in(ccm_t* self);
|
|
||||||
void ccm_add(ccm_t* self);
|
void ccm_add(ccm_t* self);
|
||||||
void ccm_sub(ccm_t* self);
|
void ccm_sub(ccm_t* self);
|
||||||
void ccm_usub(ccm_t* self);
|
void ccm_usub(ccm_t* self);
|
||||||
|
@ -45,6 +38,5 @@ void ccm_mul(ccm_t* self);
|
||||||
void ccm_div(ccm_t* self);
|
void ccm_div(ccm_t* self);
|
||||||
void ccm_mod(ccm_t* self);
|
void ccm_mod(ccm_t* self);
|
||||||
void ccm_pow(ccm_t* self);
|
void ccm_pow(ccm_t* self);
|
||||||
void ccm_not(ccm_t* self);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -13,9 +13,6 @@
|
||||||
#define CCM_ENUM_ENUM(X) X
|
#define CCM_ENUM_ENUM(X) X
|
||||||
#define CCM_ENUM_STR(X) #X
|
#define CCM_ENUM_STR(X) #X
|
||||||
|
|
||||||
#define CCM_DEBUG(TARGET, FUN) \
|
|
||||||
{char m[CCM_STRLEN]; FUN(TARGET, m, CCM_STRLEN), printf("%s\n", m);}
|
|
||||||
|
|
||||||
#define CCM_ENUM_H(PREFIX, DEF) \
|
#define CCM_ENUM_H(PREFIX, DEF) \
|
||||||
typedef enum { DEF(CCM_ENUM_ENUM) } PREFIX ; \
|
typedef enum { DEF(CCM_ENUM_ENUM) } PREFIX ; \
|
||||||
extern char const* PREFIX ## Str []
|
extern char const* PREFIX ## Str []
|
||||||
|
|
125
lib/compiler.c
125
lib/compiler.c
|
@ -26,38 +26,8 @@ void compiler_compile(compiler_t* self,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ccm_t* ccm = &self->module->ccm;
|
|
||||||
|
|
||||||
switch (node->kind)
|
switch (node->kind)
|
||||||
{
|
{
|
||||||
case NODE_INDEX: {
|
|
||||||
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_INDEX, node->children.size - 1);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case NODE_IN: {
|
|
||||||
compiler_compile(self, node->children.data[0], prog);
|
|
||||||
compiler_compile(self, node->children.data[1], prog);
|
|
||||||
|
|
||||||
prog_add_instr(prog, OP_IN, CCM_NO_PARAM);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case NODE_AND: {
|
|
||||||
compiler_compile_and(self, node, prog);
|
|
||||||
} break;
|
|
||||||
case NODE_OR: {
|
|
||||||
compiler_compile_or(self, node, prog);
|
|
||||||
} break;
|
|
||||||
case NODE_NOT: {
|
|
||||||
compiler_compile(self, node->children.data[0], prog);
|
|
||||||
prog_add_instr(prog, OP_NOT, CCM_NO_PARAM);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case NODE_ASSERT_EQ: {
|
case NODE_ASSERT_EQ: {
|
||||||
compiler_compile(self, node->children.data[0], prog);
|
compiler_compile(self, node->children.data[0], prog);
|
||||||
prog_add_instr(prog, OP_ASSERT_EQ, CCM_NO_PARAM);
|
prog_add_instr(prog, OP_ASSERT_EQ, CCM_NO_PARAM);
|
||||||
|
@ -101,13 +71,6 @@ void compiler_compile(compiler_t* self,
|
||||||
prog_add_instr(prog, OP_MK_TUPLE, node->children.size);
|
prog_add_instr(prog, OP_MK_TUPLE, node->children.size);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case NODE_BOOL: {
|
|
||||||
int val = strcmp(node->value, "true") == 0;
|
|
||||||
CCM value = ccm_to_boolean(ccm, val, node->line);
|
|
||||||
size_t id = prog_add_new_constant(prog, value);
|
|
||||||
prog_add_instr(prog, OP_PUSH, id);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case NODE_SUB: {
|
case NODE_SUB: {
|
||||||
compiler_compile(self, node->children.data[0], prog);
|
compiler_compile(self, node->children.data[0], prog);
|
||||||
|
|
||||||
|
@ -151,91 +114,3 @@ void compiler_compile(compiler_t* self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void compiler_compile_or(compiler_t* self,
|
|
||||||
node_t* node,
|
|
||||||
prog_t* prog)
|
|
||||||
{
|
|
||||||
ccm_t* ccm = &self->module->ccm;
|
|
||||||
vec_t to_true;
|
|
||||||
vec_init(&to_true);
|
|
||||||
|
|
||||||
for (size_t i=0; i<node->children.size; i++)
|
|
||||||
{
|
|
||||||
compiler_compile(self, node->children.data[i], prog);
|
|
||||||
prog_add_instr(prog, OP_NOT, 0);
|
|
||||||
size_t addr = prog_add_instr(prog, OP_BRF, 0);
|
|
||||||
vec_push(&to_true, (void*) addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FALSE
|
|
||||||
prog_add_instr(prog, OP_PUSH, prog_add_new_constant(
|
|
||||||
prog,
|
|
||||||
ccm_to_boolean(ccm, 0, node->line)
|
|
||||||
));
|
|
||||||
|
|
||||||
size_t to_end = prog_add_instr(prog, OP_BR, 0);
|
|
||||||
|
|
||||||
// TRUE
|
|
||||||
size_t true_point = prog->instrs.size;
|
|
||||||
prog_add_instr(prog, OP_PUSH, prog_add_new_constant(
|
|
||||||
prog,
|
|
||||||
ccm_to_boolean(ccm, 1, node->line)
|
|
||||||
));
|
|
||||||
|
|
||||||
for (size_t i=0; i<to_true.size; i++)
|
|
||||||
{
|
|
||||||
size_t k = ((size_t) to_true.data[i]);
|
|
||||||
((instr_t*) prog->instrs.data[k])->param = true_point;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t end_point = prog->instrs.size;
|
|
||||||
((instr_t*) prog->instrs.data[to_end])->param = end_point;
|
|
||||||
|
|
||||||
vec_free(&to_true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void compiler_compile_and(compiler_t* self,
|
|
||||||
node_t* node,
|
|
||||||
prog_t* prog)
|
|
||||||
{
|
|
||||||
ccm_t* ccm = &self->module->ccm;
|
|
||||||
vec_t to_false;
|
|
||||||
vec_init(&to_false);
|
|
||||||
|
|
||||||
for (size_t i=0; i<node->children.size; i++)
|
|
||||||
{
|
|
||||||
compiler_compile(self, node->children.data[i], prog);
|
|
||||||
size_t addr = prog_add_instr(prog, OP_BRF, 0);
|
|
||||||
vec_push(&to_false, (void*) addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TRUE
|
|
||||||
prog_add_instr(prog, OP_PUSH, prog_add_new_constant(
|
|
||||||
prog,
|
|
||||||
ccm_to_boolean(ccm, 1, node->line)
|
|
||||||
));
|
|
||||||
|
|
||||||
size_t to_end = prog_add_instr(prog, OP_BR, 0);
|
|
||||||
|
|
||||||
// FALSE
|
|
||||||
size_t false_point = prog_add_instr(
|
|
||||||
prog,
|
|
||||||
OP_PUSH,
|
|
||||||
prog_add_new_constant(
|
|
||||||
prog,
|
|
||||||
ccm_to_boolean(ccm, 0, node->line)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
for (size_t i=0; i<to_false.size; i++)
|
|
||||||
{
|
|
||||||
size_t k = ((size_t) to_false.data[i]);
|
|
||||||
((instr_t*) prog->instrs.data[k])->param = false_point;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t end_point = prog->instrs.size;
|
|
||||||
((instr_t*) prog->instrs.data[to_end])->param = end_point;
|
|
||||||
|
|
||||||
vec_free(&to_false);
|
|
||||||
}
|
|
||||||
|
|
|
@ -19,12 +19,4 @@ void compiler_free(compiler_t* self);
|
||||||
void compiler_compile(compiler_t* self,
|
void compiler_compile(compiler_t* self,
|
||||||
node_t* node,
|
node_t* node,
|
||||||
prog_t* prog);
|
prog_t* prog);
|
||||||
|
|
||||||
void compiler_compile_or(compiler_t* self,
|
|
||||||
node_t* node,
|
|
||||||
prog_t* prog);
|
|
||||||
|
|
||||||
void compiler_compile_and(compiler_t* self,
|
|
||||||
node_t* node,
|
|
||||||
prog_t* prog);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
67
lib/exec.c
67
lib/exec.c
|
@ -44,73 +44,6 @@ void exec_instr(exec_t* self,
|
||||||
|
|
||||||
switch (op)
|
switch (op)
|
||||||
{
|
{
|
||||||
case OP_INDEX: {
|
|
||||||
CCM ccm_target = ccm_pop(ccm);
|
|
||||||
vec_t* target = ccm_from_tuple(ccm, ccm_target);
|
|
||||||
value_t* result = NULL;
|
|
||||||
|
|
||||||
for (int i=0; i<param; i++)
|
|
||||||
{
|
|
||||||
CCM ccm_idx = ccm_pop(ccm);
|
|
||||||
int idx = ccm_from_num(ccm, ccm_idx);
|
|
||||||
|
|
||||||
if (idx < 0)
|
|
||||||
{
|
|
||||||
idx = target->size + idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (idx < 0 || idx >= (ssize_t) target->size)
|
|
||||||
{
|
|
||||||
assert(target->size > 0);
|
|
||||||
err_push(&self->err, ((value_t*)target->data[0])->line,
|
|
||||||
"index out of bounds");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
value_t* val = target->data[idx];
|
|
||||||
|
|
||||||
if (i == param - 1)
|
|
||||||
{
|
|
||||||
result = val;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
target = val->data.tuple;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ccm_push(ccm, ccm_from_value(ccm, result));
|
|
||||||
self->pc++;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case OP_NOT: {
|
|
||||||
ccm_not(ccm);
|
|
||||||
self->pc++;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case OP_IN: {
|
|
||||||
ccm_in(ccm);
|
|
||||||
self->pc++;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case OP_BR: {
|
|
||||||
self->pc = param;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case OP_BRF: {
|
|
||||||
CCM value = ccm_pop(ccm);
|
|
||||||
int val = ccm_from_boolean(ccm, value);
|
|
||||||
|
|
||||||
if (val)
|
|
||||||
{
|
|
||||||
self->pc++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self->pc = param;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case OP_ASSERT_NE:
|
case OP_ASSERT_NE:
|
||||||
case OP_ASSERT_EQ: {
|
case OP_ASSERT_EQ: {
|
||||||
CCM val = ccm_pop(ccm);
|
CCM val = ccm_pop(ccm);
|
||||||
|
|
|
@ -24,8 +24,6 @@ void lexer_init(lexer_t* self)
|
||||||
lexer_add_text(self, "/", NODE_DIV);
|
lexer_add_text(self, "/", NODE_DIV);
|
||||||
lexer_add_text(self, "^", NODE_POW);
|
lexer_add_text(self, "^", NODE_POW);
|
||||||
lexer_add_text(self, "%", NODE_MOD);
|
lexer_add_text(self, "%", NODE_MOD);
|
||||||
lexer_add_text(self, "[", NODE_OSQUARE);
|
|
||||||
lexer_add_text(self, "]", NODE_CSQUARE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void lexer_free(lexer_t* self)
|
void lexer_free(lexer_t* self)
|
||||||
|
@ -161,12 +159,6 @@ node_t* lexer_try_new_next(lexer_t* self)
|
||||||
|
|
||||||
CCM_KEYWORD("assert_eq", NODE_ASSERT_EQ, 0);
|
CCM_KEYWORD("assert_eq", NODE_ASSERT_EQ, 0);
|
||||||
CCM_KEYWORD("assert_ne", NODE_ASSERT_NE, 0);
|
CCM_KEYWORD("assert_ne", NODE_ASSERT_NE, 0);
|
||||||
CCM_KEYWORD("true", NODE_BOOL, 1);
|
|
||||||
CCM_KEYWORD("false", NODE_BOOL, 1);
|
|
||||||
CCM_KEYWORD("and", NODE_AND, 0);
|
|
||||||
CCM_KEYWORD("or", NODE_OR, 0);
|
|
||||||
CCM_KEYWORD("not", NODE_NOT, 0);
|
|
||||||
CCM_KEYWORD("in", NODE_IN, 0);
|
|
||||||
|
|
||||||
if (self->cursor < (ssize_t) strlen(self->source))
|
if (self->cursor < (ssize_t) strlen(self->source))
|
||||||
{
|
{
|
||||||
|
|
|
@ -46,11 +46,6 @@ int module_load(module_t* self, char const* path)
|
||||||
|
|
||||||
node_t* ast = parser_try_new_parse(&parser);
|
node_t* ast = parser_try_new_parse(&parser);
|
||||||
|
|
||||||
if (!ast)
|
|
||||||
{
|
|
||||||
goto free_parser;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!err_is_ok(&lexer.err) || !err_is_ok(&parser.err))
|
if (!err_is_ok(&lexer.err) || !err_is_ok(&parser.err))
|
||||||
{
|
{
|
||||||
err_print_stack_trace(&lexer.err);
|
err_print_stack_trace(&lexer.err);
|
||||||
|
@ -58,6 +53,7 @@ int module_load(module_t* self, char const* path)
|
||||||
err_push(&self->err, lexer.line, "invalid module");
|
err_push(&self->err, lexer.line, "invalid module");
|
||||||
goto free_parser;
|
goto free_parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
compiler_t compiler;
|
compiler_t compiler;
|
||||||
compiler_init(&compiler, self);
|
compiler_init(&compiler, self);
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,7 @@ G(NODE_MODULE), \
|
||||||
G(NODE_NUM), G(NODE_OPAR), G(NODE_CPAR), \
|
G(NODE_NUM), G(NODE_OPAR), G(NODE_CPAR), \
|
||||||
G(NODE_POW), G(NODE_ADD), G(NODE_SUB), G(NODE_MUL), \
|
G(NODE_POW), G(NODE_ADD), G(NODE_SUB), G(NODE_MUL), \
|
||||||
G(NODE_DIV), G(NODE_MOD), G(NODE_COMMA), G(NODE_TUPLE), \
|
G(NODE_DIV), G(NODE_MOD), G(NODE_COMMA), G(NODE_TUPLE), \
|
||||||
G(NODE_ASSERT_EQ), G(NODE_ASSERT_NE), G(NODE_BOOL), \
|
G(NODE_ASSERT_EQ), G(NODE_ASSERT_NE)
|
||||||
G(NODE_AND), G(NODE_OR), G(NODE_NOT), G(NODE_IN), \
|
|
||||||
G(NODE_OSQUARE), G(NODE_CSQUARE), G(NODE_INDEX)
|
|
||||||
|
|
||||||
CCM_ENUM_H(NodeKind, NODE_KIND);
|
CCM_ENUM_H(NodeKind, NODE_KIND);
|
||||||
|
|
||||||
|
|
196
lib/parser.c
196
lib/parser.c
|
@ -1,8 +1,6 @@
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
|
|
||||||
#define CCM_TRY(rule) parser_try_new_rule(self, rule)
|
#define CCM_TRY(rule) parser_try_new_rule(self, rule)
|
||||||
#define CCM_TRY_LL1(rule, node) \
|
|
||||||
parser_try_new_rule_ll1(self, rule, node)
|
|
||||||
|
|
||||||
void parser_init(parser_t* self, lexer_t* lexer)
|
void parser_init(parser_t* self, lexer_t* lexer)
|
||||||
{
|
{
|
||||||
|
@ -25,25 +23,6 @@ node_t* parser_try_new_parse(parser_t* self)
|
||||||
return CCM_TRY(parser_try_new_module);
|
return CCM_TRY(parser_try_new_module);
|
||||||
}
|
}
|
||||||
|
|
||||||
node_t* parser_try_new_rule_ll1(parser_t* self,
|
|
||||||
rule_ll1_t rule,
|
|
||||||
node_t* node)
|
|
||||||
{
|
|
||||||
if (!err_is_ok(&self->err))
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
lexer_state_t state = lexer_state(self->lexer);
|
|
||||||
|
|
||||||
node_t* result = rule(self, node);
|
|
||||||
if (result) { return result; }
|
|
||||||
|
|
||||||
lexer_restore(self->lexer, state);
|
|
||||||
|
|
||||||
return (void*) NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_t* parser_try_new_rule(parser_t* self, rule_t rule)
|
node_t* parser_try_new_rule(parser_t* self, rule_t rule)
|
||||||
{
|
{
|
||||||
if (!err_is_ok(&self->err))
|
if (!err_is_ok(&self->err))
|
||||||
|
@ -120,7 +99,7 @@ node_t* parser_try_new_expr(parser_t* self)
|
||||||
return CCM_TRY(parser_try_new_assert);
|
return CCM_TRY(parser_try_new_assert);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CCM_TRY(parser_try_new_in);
|
return CCM_TRY(parser_try_new_term);
|
||||||
}
|
}
|
||||||
|
|
||||||
node_t* parser_try_new_assert(parser_t* self)
|
node_t* parser_try_new_assert(parser_t* self)
|
||||||
|
@ -159,96 +138,6 @@ node_t* parser_try_new_assert(parser_t* self)
|
||||||
return node;
|
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);
|
|
||||||
node_t* lhs = CCM_TRY(parser_try_new_and);
|
|
||||||
if (!lhs) { return NULL; }
|
|
||||||
|
|
||||||
while (lexer_peek_kind(self->lexer, NODE_OR, 0))
|
|
||||||
{
|
|
||||||
lexer_consume_next(self->lexer, NODE_OR);
|
|
||||||
node_t* node = malloc(sizeof(node_t));
|
|
||||||
node_init(node, NODE_OR, "", lhs->line);
|
|
||||||
|
|
||||||
node_push_new_child(node, lhs);
|
|
||||||
node_t* rhs = CCM_TRY(parser_try_new_and);
|
|
||||||
|
|
||||||
if (!rhs)
|
|
||||||
{
|
|
||||||
node_free(lhs);
|
|
||||||
free(lhs);
|
|
||||||
node_free(node);
|
|
||||||
free(node);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
node_push_new_child(node, rhs);
|
|
||||||
lhs = node;
|
|
||||||
}
|
|
||||||
|
|
||||||
return lhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_t* parser_try_new_and(parser_t* self)
|
|
||||||
{
|
|
||||||
assert(self);
|
|
||||||
node_t* lhs = CCM_TRY(parser_try_new_term);
|
|
||||||
if (!lhs) { return NULL; }
|
|
||||||
|
|
||||||
while (lexer_peek_kind(self->lexer, NODE_AND, 0))
|
|
||||||
{
|
|
||||||
lexer_consume_next(self->lexer, NODE_AND);
|
|
||||||
node_t* node = malloc(sizeof(node_t));
|
|
||||||
node_init(node, NODE_AND, "", lhs->line);
|
|
||||||
|
|
||||||
node_push_new_child(node, lhs);
|
|
||||||
node_t* rhs = CCM_TRY(parser_try_new_term);
|
|
||||||
|
|
||||||
if (!rhs)
|
|
||||||
{
|
|
||||||
node_free(lhs);
|
|
||||||
free(lhs);
|
|
||||||
node_free(node);
|
|
||||||
free(node);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
node_push_new_child(node, rhs);
|
|
||||||
lhs = node;
|
|
||||||
}
|
|
||||||
|
|
||||||
return lhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_t* parser_try_new_term(parser_t* self)
|
node_t* parser_try_new_term(parser_t* self)
|
||||||
{
|
{
|
||||||
assert(self);
|
assert(self);
|
||||||
|
@ -334,38 +223,6 @@ node_t* parser_try_new_usub(parser_t* self)
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
return CCM_TRY(parser_try_new_not);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_t* parser_try_new_not(parser_t* self)
|
|
||||||
{
|
|
||||||
assert(self);
|
|
||||||
|
|
||||||
if (lexer_peek_kind(self->lexer, NODE_NOT, 0))
|
|
||||||
{
|
|
||||||
lexer_consume_next(self->lexer, NODE_NOT);
|
|
||||||
node_t* node = malloc(sizeof(node_t));
|
|
||||||
node_init(node, NODE_NOT, "", self->lexer->line);
|
|
||||||
|
|
||||||
node_t* rhs = CCM_TRY(parser_try_new_not);
|
|
||||||
|
|
||||||
if (!rhs)
|
|
||||||
{
|
|
||||||
node_free(node);
|
|
||||||
free(node);
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_push_new_child(node, rhs);
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
return CCM_TRY(parser_try_new_pow);
|
return CCM_TRY(parser_try_new_pow);
|
||||||
}
|
}
|
||||||
|
@ -419,11 +276,6 @@ node_t* parser_try_new_literal(parser_t* self)
|
||||||
|
|
||||||
if (tuple)
|
if (tuple)
|
||||||
{
|
{
|
||||||
if (lexer_peek_kind(self->lexer, NODE_OSQUARE, 0))
|
|
||||||
{
|
|
||||||
return CCM_TRY_LL1(parser_try_new_index, tuple);
|
|
||||||
}
|
|
||||||
|
|
||||||
return tuple;
|
return tuple;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -451,44 +303,6 @@ node_t* parser_try_new_literal(parser_t* self)
|
||||||
return CCM_TRY(parser_try_new_builtin);
|
return CCM_TRY(parser_try_new_builtin);
|
||||||
}
|
}
|
||||||
|
|
||||||
node_t* parser_try_new_index(parser_t* self, node_t* target)
|
|
||||||
{
|
|
||||||
assert(self);
|
|
||||||
|
|
||||||
lexer_consume_next(self->lexer, NODE_OSQUARE);
|
|
||||||
|
|
||||||
node_t* node = malloc(sizeof(node_t));
|
|
||||||
node_init(node, NODE_INDEX, "", target->line);
|
|
||||||
|
|
||||||
node_push_new_child(node, target);
|
|
||||||
|
|
||||||
while (!lexer_peek_kind(self->lexer, NODE_CSQUARE, 0))
|
|
||||||
{
|
|
||||||
node_t* element = CCM_TRY(parser_try_new_expr);
|
|
||||||
if (!element)
|
|
||||||
{
|
|
||||||
node_free(node); free(node);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_push_new_child(node, element);
|
|
||||||
|
|
||||||
if (!lexer_peek_kind(self->lexer, NODE_CSQUARE, 0))
|
|
||||||
{
|
|
||||||
lexer_consume_next(self->lexer, NODE_COMMA);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lexer_consume_next(self->lexer, NODE_CSQUARE);
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_t* parser_try_new_expr_lst(parser_t* self)
|
|
||||||
{
|
|
||||||
assert(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
node_t* parser_try_new_tuple(parser_t* self)
|
node_t* parser_try_new_tuple(parser_t* self)
|
||||||
{
|
{
|
||||||
assert(self);
|
assert(self);
|
||||||
|
@ -549,13 +363,9 @@ node_t* parser_try_new_builtin(parser_t* self)
|
||||||
assert(self);
|
assert(self);
|
||||||
|
|
||||||
node_t* node = lexer_try_new_next(self->lexer);
|
node_t* node = lexer_try_new_next(self->lexer);
|
||||||
|
parser_ensure(self, node, NODE_NUM);
|
||||||
|
|
||||||
if (node &&
|
if (node && node->kind == NODE_NUM)
|
||||||
(
|
|
||||||
node->kind == NODE_NUM
|
|
||||||
|| node->kind == NODE_BOOL
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,30 +13,22 @@ typedef struct {
|
||||||
} parser_t;
|
} parser_t;
|
||||||
|
|
||||||
typedef node_t* (*rule_t)(parser_t*);
|
typedef node_t* (*rule_t)(parser_t*);
|
||||||
typedef node_t* (*rule_ll1_t)(parser_t*, node_t*);
|
|
||||||
|
|
||||||
void parser_init(parser_t* self, lexer_t* lexer);
|
void parser_init(parser_t* self, lexer_t* lexer);
|
||||||
void parser_free(parser_t* self);
|
void parser_free(parser_t* self);
|
||||||
|
|
||||||
node_t* parser_try_new_parse(parser_t* self);
|
node_t* parser_try_new_parse(parser_t* self);
|
||||||
node_t* parser_try_new_rule(parser_t* self, rule_t rule);
|
node_t* parser_try_new_rule(parser_t* self, rule_t rule);
|
||||||
node_t* parser_try_new_rule_ll1(parser_t* self, rule_ll1_t rule, node_t* node);
|
|
||||||
int parser_ensure(parser_t* self, node_t* node, NodeKind kind);
|
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_module(parser_t* self);
|
||||||
node_t* parser_try_new_expr(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_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_term(parser_t* self);
|
node_t* parser_try_new_term(parser_t* self);
|
||||||
node_t* parser_try_new_factor(parser_t* self);
|
node_t* parser_try_new_factor(parser_t* self);
|
||||||
node_t* parser_try_new_usub(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_pow(parser_t* self);
|
||||||
node_t* parser_try_new_literal(parser_t* self);
|
node_t* parser_try_new_literal(parser_t* self);
|
||||||
node_t* parser_try_new_index(parser_t* self, node_t* target);
|
|
||||||
node_t* parser_try_new_expr_lst(parser_t* self);
|
|
||||||
node_t* parser_try_new_tuple(parser_t* self);
|
node_t* parser_try_new_tuple(parser_t* self);
|
||||||
node_t* parser_try_new_builtin(parser_t* self);
|
node_t* parser_try_new_builtin(parser_t* self);
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,7 @@
|
||||||
|
|
||||||
#define TYPES(G) \
|
#define TYPES(G) \
|
||||||
G(TYPE_NUM), \
|
G(TYPE_NUM), \
|
||||||
G(TYPE_TUPLE), \
|
G(TYPE_TUPLE)
|
||||||
G(TYPE_BOOLEAN)
|
|
||||||
|
|
||||||
CCM_ENUM_H(Type, TYPES);
|
CCM_ENUM_H(Type, TYPES);
|
||||||
|
|
||||||
|
|
20
lib/value.c
20
lib/value.c
|
@ -17,14 +17,6 @@ void value_init_new_tuple(value_t* self, vec_t* values, int line)
|
||||||
self->line = line;
|
self->line = line;
|
||||||
}
|
}
|
||||||
|
|
||||||
void value_init_boolean(value_t* self, int boolean, int line)
|
|
||||||
{
|
|
||||||
assert(self);
|
|
||||||
self->data.boolean = boolean;
|
|
||||||
self->type = TYPE_BOOLEAN;
|
|
||||||
self->line = line;
|
|
||||||
}
|
|
||||||
|
|
||||||
value_t* value_new_clone(value_t* self)
|
value_t* value_new_clone(value_t* self)
|
||||||
{
|
{
|
||||||
assert(self);
|
assert(self);
|
||||||
|
@ -36,9 +28,6 @@ value_t* value_new_clone(value_t* self)
|
||||||
case TYPE_NUM: {
|
case TYPE_NUM: {
|
||||||
value_init_num(value, self->data.num, self->line);
|
value_init_num(value, self->data.num, self->line);
|
||||||
} break;
|
} break;
|
||||||
case TYPE_BOOLEAN: {
|
|
||||||
value_init_boolean(value, self->data.boolean, self->line);
|
|
||||||
} break;
|
|
||||||
case TYPE_TUPLE: {
|
case TYPE_TUPLE: {
|
||||||
vec_t* vec = malloc(sizeof(vec_t));
|
vec_t* vec = malloc(sizeof(vec_t));
|
||||||
vec_init(vec);
|
vec_init(vec);
|
||||||
|
@ -87,11 +76,6 @@ size_t value_str(value_t* self, char* buffer, size_t size)
|
||||||
self->data.num);
|
self->data.num);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case TYPE_BOOLEAN: {
|
|
||||||
sz += snprintf(buffer + sz, size - sz, "%s",
|
|
||||||
self->data.boolean ? "true" : "false");
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case TYPE_TUPLE: {
|
case TYPE_TUPLE: {
|
||||||
sz += snprintf(buffer + sz, size - sz, "(");
|
sz += snprintf(buffer + sz, size - sz, "(");
|
||||||
|
|
||||||
|
@ -133,10 +117,6 @@ int value_equals(value_t* self, value_t* rhs)
|
||||||
return self->data.num == rhs->data.num;
|
return self->data.num == rhs->data.num;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case TYPE_BOOLEAN: {
|
|
||||||
return self->data.boolean == rhs->data.boolean;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case TYPE_TUPLE: {
|
case TYPE_TUPLE: {
|
||||||
if (self->data.tuple->size != rhs->data.tuple->size)
|
if (self->data.tuple->size != rhs->data.tuple->size)
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,7 +9,6 @@ typedef struct {
|
||||||
union {
|
union {
|
||||||
double num;
|
double num;
|
||||||
vec_t* tuple;
|
vec_t* tuple;
|
||||||
int boolean;
|
|
||||||
} data;
|
} data;
|
||||||
|
|
||||||
Type type;
|
Type type;
|
||||||
|
@ -18,7 +17,6 @@ typedef struct {
|
||||||
|
|
||||||
void value_init_num(value_t* self, double num, int line);
|
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_new_tuple(value_t* self, vec_t* values, int line);
|
||||||
void value_init_boolean(value_t* self, int boolean, int line);
|
|
||||||
value_t* value_new_clone(value_t* self);
|
value_t* value_new_clone(value_t* self);
|
||||||
void value_free(value_t* self);
|
void value_free(value_t* self);
|
||||||
|
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
assert_eq (true, true)
|
|
||||||
assert_eq (false, false)
|
|
||||||
|
|
||||||
assert_eq (true, true and true)
|
|
||||||
assert_eq (false, true and false)
|
|
||||||
assert_eq (false, false and true)
|
|
||||||
assert_eq (false, false and false)
|
|
||||||
|
|
||||||
assert_eq (true, true or true)
|
|
||||||
assert_eq (true, true or false)
|
|
||||||
assert_eq (true, false or true)
|
|
||||||
assert_eq (false, false or false)
|
|
||||||
|
|
||||||
assert_eq (true, not false)
|
|
||||||
assert_eq (false, not true)
|
|
||||||
|
|
||||||
assert_eq (true, not not true)
|
|
||||||
assert_eq (false, not not not true)
|
|
||||||
|
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
assert_eq ((3, 2), (3, 2))
|
|
||||||
assert_ne ((3, 2), (3, 4))
|
|
||||||
|
|
||||||
# IN KEYWORD
|
|
||||||
# ==========
|
|
||||||
assert_eq (true, 7 in (2, 7, 6))
|
|
||||||
assert_eq (false, 7 in (2, 2, 6))
|
|
||||||
|
|
||||||
assert_eq (true, (2, 3) in (1, (2, 3)))
|
|
||||||
assert_eq (false, (2, 3) in (1, (2, 4)))
|
|
||||||
|
|
||||||
# INDEX
|
|
||||||
# =====
|
|
||||||
assert_eq (2, (2, 7)[0])
|
|
||||||
assert_eq (7, (2, 7)[1])
|
|
||||||
assert_eq (9, (2, 7, 9)[-1])
|
|
||||||
assert_eq (7, (2, 7, 9)[-2])
|
|
||||||
assert_eq (2, (2, 7, 9)[-3])
|
|
||||||
assert_eq (1, ((1, 2), (3, 4))[0, 0])
|
|
||||||
assert_eq (2, ((1, 2), (3, 4))[0, 1])
|
|
||||||
assert_eq (3, ((1, 2), (3, 4))[1, 0])
|
|
||||||
assert_eq (4, ((1, 2), (3, 4))[1, 1])
|
|
||||||
|
|
||||||
assert_eq (false, 23 in (2, (7, 23, 2), 9))
|
|
||||||
assert_eq (true, 23 in (2, (7, 23, 2), 9)[1])
|
|
Loading…
Reference in New Issue