✨ vars assignment.
parent
a19071bf1d
commit
d67466cad6
|
@ -11,9 +11,10 @@ int main(int argc, char** argv)
|
||||||
module_t module;
|
module_t module;
|
||||||
module_init(&module);
|
module_init(&module);
|
||||||
|
|
||||||
module_load(&module, argv[1]);
|
status = module_load(&module, argv[1]);
|
||||||
|
|
||||||
if (!err_is_ok(&module.err))
|
|
||||||
|
if (status != 0 || !err_is_ok(&module.err))
|
||||||
{
|
{
|
||||||
err_print_stack_trace(&module.err);
|
err_print_stack_trace(&module.err);
|
||||||
status = 1;
|
status = 1;
|
||||||
|
|
|
@ -3,6 +3,8 @@ EXPR ::=
|
||||||
| OR
|
| OR
|
||||||
| ASSERT
|
| ASSERT
|
||||||
| DECL
|
| DECL
|
||||||
|
| ASSIGN
|
||||||
|
ASSIGN ::= (ident|INDEX) assign EXPR
|
||||||
DECL ::= var ident assign EXPR
|
DECL ::= var ident assign EXPR
|
||||||
ASSERT ::= (assert_eq|assert_ne) tuple
|
ASSERT ::= (assert_eq|assert_ne) tuple
|
||||||
OR ::= AND (or AND)*
|
OR ::= AND (or AND)*
|
||||||
|
|
|
@ -10,7 +10,7 @@ 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_BRF), G(OP_BR), \
|
||||||
G(OP_NOT), G(OP_IN), G(OP_INDEX), G(OP_EQ), G(OP_LT), \
|
G(OP_NOT), G(OP_IN), G(OP_INDEX), G(OP_EQ), G(OP_LT), \
|
||||||
G(OP_GT), G(OP_MK_ARRAY), G(OP_LOCAL_STORE), \
|
G(OP_GT), G(OP_MK_ARRAY), G(OP_LOCAL_STORE), \
|
||||||
G(OP_LOCAL_LOAD)
|
G(OP_LOCAL_LOAD), G(OP_ASTORE)
|
||||||
|
|
||||||
CCM_ENUM_H(Opcode, OPCODES);
|
CCM_ENUM_H(Opcode, OPCODES);
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#define CCM_STRLEN 256
|
#define CCM_STRLEN 4096
|
||||||
#define CCM_ENUM_ENUM(X) X
|
#define CCM_ENUM_ENUM(X) X
|
||||||
#define CCM_ENUM_STR(X) #X
|
#define CCM_ENUM_STR(X) #X
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,57 @@ void compiler_compile(compiler_t* self,
|
||||||
|
|
||||||
switch (node->kind)
|
switch (node->kind)
|
||||||
{
|
{
|
||||||
|
case NODE_ASSIGN: {
|
||||||
|
node_t* target = node->children.data[0];
|
||||||
|
node_t* expr = node->children.data[1];
|
||||||
|
|
||||||
|
if (target->kind == NODE_INDEX) {
|
||||||
|
node_t* ident = target->children.data[0];
|
||||||
|
|
||||||
|
sym_entry_t const* entry = sym_try_get_value(
|
||||||
|
sym,
|
||||||
|
ident->value
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!entry) {
|
||||||
|
err_push(&self->err, ident->line,
|
||||||
|
"undefined array '%s'", ident->value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
compiler_compile(self, expr, prog);
|
||||||
|
size_t indexes_count = target->children.size - 1;
|
||||||
|
|
||||||
|
for (size_t i=1; i<target->children.size; i++) {
|
||||||
|
compiler_compile(
|
||||||
|
self,
|
||||||
|
target->children.data[i],
|
||||||
|
prog
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
prog_add_instr(prog, OP_PUSH, entry->local_addr);
|
||||||
|
prog_add_instr(prog, OP_ASTORE, indexes_count);
|
||||||
|
} else {
|
||||||
|
compiler_compile(self, expr, prog);
|
||||||
|
int status =
|
||||||
|
sym_try_assign(sym, target->value, self->loc_counter);
|
||||||
|
|
||||||
|
if (!status)
|
||||||
|
{
|
||||||
|
err_push(&self->err, target->line,
|
||||||
|
"cannot assign value to '%s'",
|
||||||
|
target->value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
prog_add_instr(prog, OP_LOCAL_STORE, self->loc_counter);
|
||||||
|
|
||||||
|
self->loc_counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
} break;
|
||||||
|
|
||||||
case NODE_VARDECL: {
|
case NODE_VARDECL: {
|
||||||
node_t const* ident = node->children.data[0];
|
node_t const* ident = node->children.data[0];
|
||||||
node_t* expr = node->children.data[1];
|
node_t* expr = node->children.data[1];
|
||||||
|
|
41
lib/exec.c
41
lib/exec.c
|
@ -70,7 +70,11 @@ void exec_instr(exec_t* self,
|
||||||
vec_push(vec, ccm_to_value(ccm, element));
|
vec_push(vec, ccm_to_value(ccm, element));
|
||||||
}
|
}
|
||||||
|
|
||||||
int line = ((value_t*) vec->data[0])->line;
|
int line = 0;
|
||||||
|
|
||||||
|
if (vec->size > 0) {
|
||||||
|
line = ((value_t*) vec->data[0])->line;
|
||||||
|
}
|
||||||
|
|
||||||
size_t addr = ccm_store_global(
|
size_t addr = ccm_store_global(
|
||||||
ccm,
|
ccm,
|
||||||
|
@ -96,6 +100,36 @@ void exec_instr(exec_t* self,
|
||||||
self->pc++;
|
self->pc++;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case OP_ASTORE: {
|
||||||
|
int addr = ccm_pop(ccm);
|
||||||
|
CCM ccm_gaddr = ccm_get(ccm, addr);
|
||||||
|
int gaddr = ccm_from_ref(ccm, ccm_gaddr);
|
||||||
|
|
||||||
|
CCM ccm_target = ccm_load_global(ccm, gaddr);
|
||||||
|
value_t* target = ccm_to_value(ccm, ccm_target);
|
||||||
|
|
||||||
|
int indexes[param];
|
||||||
|
memset(indexes, 0, param * sizeof(int));
|
||||||
|
|
||||||
|
for (int i=0; i<param; i++) {
|
||||||
|
CCM ccm_idx = ccm_pop(ccm);
|
||||||
|
int idx = ccm_from_num(ccm, ccm_idx);
|
||||||
|
indexes[param - 1 - i] = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=0; i<param-1; i++)
|
||||||
|
{
|
||||||
|
target = target->data.array->data[indexes[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
CCM ccm_expr = ccm_pop(ccm);
|
||||||
|
value_t* expr = ccm_to_value(ccm, ccm_expr);
|
||||||
|
target->data.array->data[indexes[param - 1]]
|
||||||
|
= expr;
|
||||||
|
ccm_push(ccm, ccm_expr);
|
||||||
|
self->pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
case OP_INDEX: {
|
case OP_INDEX: {
|
||||||
CCM ccm_target = ccm_pop(ccm);
|
CCM ccm_target = ccm_pop(ccm);
|
||||||
|
|
||||||
|
@ -192,7 +226,10 @@ void exec_instr(exec_t* self,
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
assert(0);
|
value_t const* val = ccm_to_value(ccm, ccm_target);
|
||||||
|
err_push(&self->err, val->line,
|
||||||
|
"not an array");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self->pc++;
|
self->pc++;
|
||||||
|
|
14
lib/module.c
14
lib/module.c
|
@ -34,6 +34,7 @@ int module_load(module_t* self, char const* path)
|
||||||
{
|
{
|
||||||
assert(self);
|
assert(self);
|
||||||
assert(path);
|
assert(path);
|
||||||
|
int status = 0;
|
||||||
|
|
||||||
if (module_load_source(self, path) != 0)
|
if (module_load_source(self, path) != 0)
|
||||||
{
|
{
|
||||||
|
@ -54,19 +55,28 @@ int module_load(module_t* self, char const* path)
|
||||||
err_print_stack_trace(&lexer.err);
|
err_print_stack_trace(&lexer.err);
|
||||||
err_print_stack_trace(&parser.err);
|
err_print_stack_trace(&parser.err);
|
||||||
err_push(&self->err, lexer.line, "invalid module");
|
err_push(&self->err, lexer.line, "invalid module");
|
||||||
|
status = 1;
|
||||||
goto free_parser;
|
goto free_parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ast)
|
if (!ast)
|
||||||
{
|
{
|
||||||
|
status = 1;
|
||||||
goto free_parser;
|
goto free_parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
compiler_t compiler;
|
compiler_t compiler;
|
||||||
compiler_init(&compiler, self);
|
compiler_init(&compiler, self);
|
||||||
|
|
||||||
compiler_compile(&compiler, ast, &self->prog);
|
compiler_compile(&compiler, ast, &self->prog);
|
||||||
|
|
||||||
|
if (!err_is_ok(&compiler.err))
|
||||||
|
{
|
||||||
|
err_print_stack_trace(&compiler.err);
|
||||||
|
status = 1;
|
||||||
|
goto free_compiler;
|
||||||
|
}
|
||||||
|
|
||||||
|
free_compiler:
|
||||||
compiler_free(&compiler);
|
compiler_free(&compiler);
|
||||||
node_free(ast);
|
node_free(ast);
|
||||||
free(ast);
|
free(ast);
|
||||||
|
@ -74,7 +84,7 @@ free_parser:
|
||||||
parser_free(&parser);
|
parser_free(&parser);
|
||||||
lexer_free(&lexer);
|
lexer_free(&lexer);
|
||||||
|
|
||||||
return 0;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
int module_load_source(module_t* self, char const* path)
|
int module_load_source(module_t* self, char const* path)
|
||||||
|
|
123
lib/parser.c
123
lib/parser.c
|
@ -146,9 +146,48 @@ node_t* parser_try_new_expr(parser_t* self)
|
||||||
return CCM_TRY(parser_try_new_assert);
|
return CCM_TRY(parser_try_new_assert);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node_t* assign = CCM_TRY(parser_try_new_assign);
|
||||||
|
if (assign) { return assign; }
|
||||||
|
|
||||||
return CCM_TRY(parser_try_new_or);
|
return CCM_TRY(parser_try_new_or);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node_t* parser_try_new_assign(parser_t* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
node_t* target = CCM_TRY(parser_try_new_index);
|
||||||
|
|
||||||
|
if (!target && lexer_peek_kind(self->lexer, NODE_IDENT, 0)) {
|
||||||
|
target = lexer_try_new_next(self->lexer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lexer_peek_kind(self->lexer, NODE_ASSIGN, 0))
|
||||||
|
{
|
||||||
|
node_free(target); free(target);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
lexer_consume_next(self->lexer, NODE_ASSIGN);
|
||||||
|
node_t* expr = CCM_TRY(parser_try_new_expr);
|
||||||
|
|
||||||
|
if (!expr)
|
||||||
|
{
|
||||||
|
node_free(target); free(target);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
node_t* node = malloc(sizeof(node_t));
|
||||||
|
node_init(node, NODE_ASSIGN, "", self->lexer->line);
|
||||||
|
node_push_new_child(node, target);
|
||||||
|
node_push_new_child(node, expr);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
node_t* parser_try_new_decl(parser_t* self)
|
node_t* parser_try_new_decl(parser_t* self)
|
||||||
{
|
{
|
||||||
assert(self);
|
assert(self);
|
||||||
|
@ -534,15 +573,12 @@ node_t* parser_try_new_literal(parser_t* self)
|
||||||
{
|
{
|
||||||
assert(self);
|
assert(self);
|
||||||
|
|
||||||
|
node_t* index = CCM_TRY(parser_try_new_index);
|
||||||
|
if (index) { return index; }
|
||||||
|
|
||||||
if (lexer_peek_kind(self->lexer, NODE_OSQUARE, 0))
|
if (lexer_peek_kind(self->lexer, NODE_OSQUARE, 0))
|
||||||
{
|
{
|
||||||
node_t* array = CCM_TRY(parser_try_new_array);
|
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;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -552,11 +588,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -582,14 +613,6 @@ node_t* parser_try_new_literal(parser_t* self)
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((lexer_peek_kind(self->lexer, NODE_STR, 0)
|
|
||||||
|| lexer_peek_kind(self->lexer, NODE_IDENT, 0))
|
|
||||||
&& lexer_peek_kind(self->lexer, NODE_OSQUARE, 1))
|
|
||||||
{
|
|
||||||
node_t* target = CCM_TRY(parser_try_new_builtin);
|
|
||||||
return CCM_TRY_LL1(parser_try_new_index, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
return CCM_TRY(parser_try_new_builtin);
|
return CCM_TRY(parser_try_new_builtin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -600,6 +623,12 @@ node_t* parser_try_new_array(parser_t* self)
|
||||||
node_t* node = malloc(sizeof(node_t));
|
node_t* node = malloc(sizeof(node_t));
|
||||||
node_init(node, NODE_ARRAY, "", self->lexer->line);
|
node_init(node, NODE_ARRAY, "", self->lexer->line);
|
||||||
|
|
||||||
|
if (!lexer_peek_kind(self->lexer, NODE_OSQUARE, 0))
|
||||||
|
{
|
||||||
|
node_free(node); free(node);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
lexer_consume_next(self->lexer, NODE_OSQUARE);
|
lexer_consume_next(self->lexer, NODE_OSQUARE);
|
||||||
|
|
||||||
int first = 1;
|
int first = 1;
|
||||||
|
@ -624,15 +653,50 @@ node_t* parser_try_new_array(parser_t* self)
|
||||||
first = 0;
|
first = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!lexer_peek_kind(self->lexer, NODE_CSQUARE, 0))
|
||||||
|
{
|
||||||
|
node_free(node); free(node);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
lexer_consume_next(self->lexer, NODE_CSQUARE);
|
lexer_consume_next(self->lexer, NODE_CSQUARE);
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
node_t* parser_try_new_index(parser_t* self, node_t* target)
|
node_t* parser_try_new_index(parser_t* self)
|
||||||
{
|
{
|
||||||
assert(self);
|
assert(self);
|
||||||
|
|
||||||
|
node_t* target = NULL;
|
||||||
|
|
||||||
|
if (lexer_peek_kind(self->lexer, NODE_STR, 0)
|
||||||
|
|| lexer_peek_kind(self->lexer, NODE_IDENT, 0))
|
||||||
|
{
|
||||||
|
target = lexer_try_new_next(self->lexer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!target)
|
||||||
|
{
|
||||||
|
target = CCM_TRY(parser_try_new_tuple);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!target)
|
||||||
|
{
|
||||||
|
target = CCM_TRY(parser_try_new_array);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lexer_peek_kind(self->lexer, NODE_OSQUARE, 0))
|
||||||
|
{
|
||||||
|
node_free(target); free(target);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
lexer_consume_next(self->lexer, NODE_OSQUARE);
|
lexer_consume_next(self->lexer, NODE_OSQUARE);
|
||||||
|
|
||||||
node_t* node = malloc(sizeof(node_t));
|
node_t* node = malloc(sizeof(node_t));
|
||||||
|
@ -646,6 +710,7 @@ node_t* parser_try_new_index(parser_t* self, node_t* target)
|
||||||
if (!element)
|
if (!element)
|
||||||
{
|
{
|
||||||
node_free(node); free(node);
|
node_free(node); free(node);
|
||||||
|
node_free(target); free(target);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -657,6 +722,13 @@ node_t* parser_try_new_index(parser_t* self, node_t* target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!lexer_peek_kind(self->lexer, NODE_CSQUARE, 0))
|
||||||
|
{
|
||||||
|
node_free(node); free(node);
|
||||||
|
node_free(target); free(target);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
lexer_consume_next(self->lexer, NODE_CSQUARE);
|
lexer_consume_next(self->lexer, NODE_CSQUARE);
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
|
@ -668,13 +740,15 @@ node_t* parser_try_new_tuple(parser_t* self)
|
||||||
node_t* node = malloc(sizeof(node_t));
|
node_t* node = malloc(sizeof(node_t));
|
||||||
node_init(node, NODE_TUPLE, "", self->lexer->line);
|
node_init(node, NODE_TUPLE, "", self->lexer->line);
|
||||||
|
|
||||||
if (!lexer_consume_next(self->lexer, NODE_OPAR))
|
if (!lexer_peek_kind(self->lexer, NODE_OPAR, 0))
|
||||||
{
|
{
|
||||||
node_free(node);
|
node_free(node);
|
||||||
free(node);
|
free(node);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lexer_consume_next(self->lexer, NODE_OPAR);
|
||||||
|
|
||||||
node_t* lhs = CCM_TRY(parser_try_new_expr);
|
node_t* lhs = CCM_TRY(parser_try_new_expr);
|
||||||
|
|
||||||
if (!lhs)
|
if (!lhs)
|
||||||
|
@ -705,6 +779,13 @@ node_t* parser_try_new_tuple(parser_t* self)
|
||||||
contains_more_than_one_expr = 1;
|
contains_more_than_one_expr = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!lexer_peek_kind(self->lexer, NODE_CPAR, 0))
|
||||||
|
{
|
||||||
|
node_free(node);
|
||||||
|
free(node);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
lexer_consume_next(self->lexer, NODE_CPAR);
|
lexer_consume_next(self->lexer, NODE_CPAR);
|
||||||
|
|
||||||
if (!contains_more_than_one_expr)
|
if (!contains_more_than_one_expr)
|
||||||
|
|
|
@ -25,6 +25,7 @@ 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_assign(parser_t* self);
|
||||||
node_t* parser_try_new_decl(parser_t* self);
|
node_t* parser_try_new_decl(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_or(parser_t* self);
|
node_t* parser_try_new_or(parser_t* self);
|
||||||
|
@ -39,7 +40,7 @@ node_t* parser_try_new_pow(parser_t* self);
|
||||||
node_t* parser_try_new_in(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_literal(parser_t* self);
|
||||||
node_t* parser_try_new_array(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_index(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);
|
||||||
|
|
||||||
|
|
|
@ -17,3 +17,17 @@ var f = [2, 3]
|
||||||
var g = 1
|
var g = 1
|
||||||
|
|
||||||
assert_eq (3, f[g])
|
assert_eq (3, f[g])
|
||||||
|
|
||||||
|
# ASSIGN
|
||||||
|
# ======
|
||||||
|
var h = 34
|
||||||
|
h = 37
|
||||||
|
assert_eq (37, h)
|
||||||
|
|
||||||
|
var i = [2, 3, 4]
|
||||||
|
i[2] = 7
|
||||||
|
assert_eq ([2, 3, 7], i)
|
||||||
|
|
||||||
|
var j = [[1, 2], [3, 4]]
|
||||||
|
j[1, 0] = 99
|
||||||
|
assert_eq ([[1, 2], [99, 4]], j)
|
||||||
|
|
Loading…
Reference in New Issue