Skip to content
Snippets Groups Projects
Commit 5ac1b2ef authored by Damien's avatar Damien
Browse files

Implement REPL.

parent 7bbd1106
No related branches found
No related tags found
No related merge requests found
......@@ -44,11 +44,14 @@ typedef struct _compiler_t {
qstr qstr___doc__;
qstr qstr_assertion_error;
qstr qstr_micropython;
qstr qstr_byte_code;
qstr qstr_native;
qstr qstr_viper;
qstr qstr_asm_thumb;
bool is_repl;
pass_kind_t pass;
bool had_error; // try to keep compiler clean from nlr
int next_label;
......@@ -196,7 +199,7 @@ static int comp_next_label(compiler_t *comp) {
}
static scope_t *scope_new_and_link(compiler_t *comp, scope_kind_t kind, py_parse_node_t pn, uint emit_options) {
scope_t *scope = scope_new(kind, pn, rt_get_new_unique_code_id(), emit_options);
scope_t *scope = scope_new(kind, pn, rt_get_unique_code_id(kind == SCOPE_MODULE), emit_options);
scope->parent = comp->scope_cur;
scope->next = NULL;
if (comp->scope_head == NULL) {
......@@ -855,7 +858,8 @@ static bool compile_built_in_decorator(compiler_t *comp, int name_len, py_parse_
}
qstr attr = PY_PARSE_NODE_LEAF_ARG(name_nodes[1]);
if (0) {
if (attr == comp->qstr_byte_code) {
*emit_options = EMIT_OPT_BYTE_CODE;
#if MICROPY_EMIT_NATIVE
} else if (attr == comp->qstr_native) {
*emit_options = EMIT_OPT_NATIVE_PYTHON;
......@@ -1046,7 +1050,13 @@ void compile_continue_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
}
void compile_return_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
if (comp->scope_cur->kind != SCOPE_FUNCTION) {
printf("SyntaxError: 'return' outside function\n");
comp->had_error = true;
return;
}
if (PY_PARSE_NODE_IS_NULL(pns->nodes[0])) {
// no argument to 'return', so return None
EMIT(load_const_tok, PY_TOKEN_KW_NONE);
} else if (PY_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_test_if_expr)) {
// special case when returning an if-expression; to match CPython optimisation
......@@ -1566,11 +1576,21 @@ void compile_with_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
void compile_expr_stmt(compiler_t *comp, py_parse_node_struct_t *pns) {
if (PY_PARSE_NODE_IS_NULL(pns->nodes[1])) {
if (PY_PARSE_NODE_IS_LEAF(pns->nodes[0]) && !PY_PARSE_NODE_IS_ID(pns->nodes[0])) {
// do nothing with a lonely constant
if (comp->is_repl && comp->scope_cur->kind == SCOPE_MODULE) {
// for REPL, evaluate then print the expression
EMIT(load_id, qstr_from_str_static("__repl_print__"));
compile_node(comp, pns->nodes[0]);
EMIT(call_function, 1, 0, false, false);
EMIT(pop_top);
} else {
compile_node(comp, pns->nodes[0]); // just an expression
EMIT(pop_top); // discard last result since this is a statement and leaves nothing on the stack
// for non-REPL, evaluate then discard the expression
if (PY_PARSE_NODE_IS_LEAF(pns->nodes[0]) && !PY_PARSE_NODE_IS_ID(pns->nodes[0])) {
// do nothing with a lonely constant
} else {
compile_node(comp, pns->nodes[0]); // just an expression
EMIT(pop_top); // discard last result since this is a statement and leaves nothing on the stack
}
}
} else {
py_parse_node_struct_t *pns1 = (py_parse_node_struct_t*)pns->nodes[1];
......@@ -2287,6 +2307,7 @@ void compile_node(compiler_t *comp, py_parse_node_t pn) {
case PY_PARSE_NODE_TOKEN:
if (arg == PY_TOKEN_NEWLINE) {
// this can occur when file_input lets through a NEWLINE (eg if file starts with a newline)
// or when single_input lets through a NEWLINE (user enters a blank line)
// do nothing
} else {
EMIT(load_const_tok, arg);
......@@ -2299,7 +2320,7 @@ void compile_node(compiler_t *comp, py_parse_node_t pn) {
compile_function_t f = compile_function[PY_PARSE_NODE_STRUCT_KIND(pns)];
if (f == NULL) {
printf("node %u cannot be compiled\n", (uint)PY_PARSE_NODE_STRUCT_KIND(pns));
parse_node_show(pn, 0);
py_parse_node_show(pn, 0);
assert(0);
} else {
f(comp, pns);
......@@ -2481,14 +2502,17 @@ void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) {
scope->stack_size = 0;
}
#if MICROPY_EMIT_CPYTHON
if (comp->pass == PASS_3) {
//printf("----\n");
scope_print_info(scope);
}
#endif
// compile
if (scope->kind == SCOPE_MODULE) {
check_for_doc_string(comp, scope->pn);
if (!comp->is_repl) {
check_for_doc_string(comp, scope->pn);
}
compile_node(comp, scope->pn);
EMIT(load_const_tok, PY_TOKEN_KW_NONE);
EMIT(return_value);
......@@ -2731,7 +2755,7 @@ void compile_scope_compute_things(compiler_t *comp, scope_t *scope) {
}
}
void py_compile(py_parse_node_t pn) {
bool py_compile(py_parse_node_t pn, bool is_repl) {
compiler_t *comp = m_new(compiler_t, 1);
comp->qstr___class__ = qstr_from_str_static("__class__");
......@@ -2742,10 +2766,14 @@ void py_compile(py_parse_node_t pn) {
comp->qstr___doc__ = qstr_from_str_static("__doc__");
comp->qstr_assertion_error = qstr_from_str_static("AssertionError");
comp->qstr_micropython = qstr_from_str_static("micropython");
comp->qstr_byte_code = qstr_from_str_static("byte_code");
comp->qstr_native = qstr_from_str_static("native");
comp->qstr_viper = qstr_from_str_static("viper");
comp->qstr_asm_thumb = qstr_from_str_static("asm_thumb");
comp->is_repl = is_repl;
comp->had_error = false;
comp->break_label = 0;
comp->continue_label = 0;
comp->except_nest_level = 0;
......@@ -2764,7 +2792,7 @@ void py_compile(py_parse_node_t pn) {
comp->emit_inline_asm = NULL;
comp->emit_inline_asm_method_table = NULL;
uint max_num_labels = 0;
for (scope_t *s = comp->scope_head; s != NULL; s = s->next) {
for (scope_t *s = comp->scope_head; s != NULL && !comp->had_error; s = s->next) {
if (false) {
#if MICROPY_EMIT_INLINE_THUMB
} else if (s->emit_options == EMIT_OPT_ASM_THUMB) {
......@@ -2781,7 +2809,7 @@ void py_compile(py_parse_node_t pn) {
}
// compute some things related to scope and identifiers
for (scope_t *s = comp->scope_head; s != NULL; s = s->next) {
for (scope_t *s = comp->scope_head; s != NULL && !comp->had_error; s = s->next) {
compile_scope_compute_things(comp, s);
}
......@@ -2796,7 +2824,7 @@ void py_compile(py_parse_node_t pn) {
#if MICROPY_EMIT_INLINE_THUMB
emit_inline_asm_t *emit_inline_thumb = NULL;
#endif
for (scope_t *s = comp->scope_head; s != NULL; s = s->next) {
for (scope_t *s = comp->scope_head; s != NULL && !comp->had_error; s = s->next) {
if (false) {
// dummy
......@@ -2857,4 +2885,6 @@ void py_compile(py_parse_node_t pn) {
}
m_free(comp);
return !comp->had_error;
}
void py_compile(py_parse_node_t pn);
bool py_compile(py_parse_node_t pn, bool is_repl);
......@@ -128,7 +128,7 @@ py_parse_node_struct_t *parse_node_new_struct(int rule_id, int num_args) {
return pn;
}
void parse_node_show(py_parse_node_t pn, int indent) {
void py_parse_node_show(py_parse_node_t pn, int indent) {
for (int i = 0; i < indent; i++) {
printf(" ");
}
......@@ -155,7 +155,7 @@ void parse_node_show(py_parse_node_t pn, int indent) {
printf("rule(%u) (n=%d)\n", (uint)PY_PARSE_NODE_STRUCT_KIND(pns2), n);
#endif
for (int i = 0; i < n; i++) {
parse_node_show(pns2->nodes[i], indent + 2);
py_parse_node_show(pns2->nodes[i], indent + 2);
}
}
}
......@@ -164,7 +164,7 @@ void parse_node_show(py_parse_node_t pn, int indent) {
static void result_stack_show(parser_t *parser) {
printf("result stack, most recent first\n");
for (int i = parser->result_stack_top - 1; i >= 0; i--) {
parse_node_show(parser->result_stack[i], 0);
py_parse_node_show(parser->result_stack[i], 0);
}
}
*/
......@@ -251,8 +251,7 @@ static void push_result_rule(parser_t *parser, rule_t *rule, int num_args) {
push_result_node(parser, (py_parse_node_t)pn);
}
py_parse_node_t py_parse(py_lexer_t *lex, int wanted_rule) {
wanted_rule = RULE_file_input;
py_parse_node_t py_parse(py_lexer_t *lex, py_parse_input_kind_t input_kind) {
parser_t *parser = m_new(parser_t, 1);
parser->rule_stack_alloc = 64;
parser->rule_stack_top = 0;
......@@ -261,7 +260,13 @@ py_parse_node_t py_parse(py_lexer_t *lex, int wanted_rule) {
parser->result_stack = m_new(py_parse_node_t, 1000);
parser->result_stack_top = 0;
push_rule(parser, rules[wanted_rule], 0);
int top_level_rule;
switch (input_kind) {
case PY_PARSE_SINGLE_INPUT: top_level_rule = RULE_single_input; break;
//case PY_PARSE_EVAL_INPUT: top_level_rule = RULE_eval_input; break;
default: top_level_rule = RULE_file_input;
}
push_rule(parser, rules[top_level_rule], 0);
uint n, i;
bool backtrack = false;
......
......@@ -54,6 +54,12 @@ typedef struct _py_parse_node_struct_t {
py_parse_node_t py_parse_node_new_leaf(machine_int_t kind, machine_int_t arg);
void parse_node_show(py_parse_node_t pn, int indent);
void py_parse_node_show(py_parse_node_t pn, int indent);
py_parse_node_t py_parse(struct _py_lexer_t *lex, int wanted_rule);
typedef enum {
PY_PARSE_SINGLE_INPUT,
PY_PARSE_FILE_INPUT,
PY_PARSE_EVAL_INPUT,
} py_parse_input_kind_t;
py_parse_node_t py_parse(struct _py_lexer_t *lex, py_parse_input_kind_t input_kind);
......@@ -192,11 +192,7 @@ void py_map_init(py_map_t *map, py_map_kind_t kind, int n) {
map->kind = kind;
map->alloc = get_doubling_prime_greater_or_equal_to(n + 1);
map->used = 0;
map->table = m_new(py_map_elem_t, map->alloc);
for (int i = 0; i < map->alloc; i++) {
map->table[i].key = NULL;
map->table[i].value = NULL;
}
map->table = m_new0(py_map_elem_t, map->alloc);
}
py_map_t *py_map_new(py_map_kind_t kind, int n) {
......@@ -205,9 +201,15 @@ py_map_t *py_map_new(py_map_kind_t kind, int n) {
return map;
}
int py_obj_hash(py_obj_t o_in) {
if (IS_SMALL_INT(o_in)) {
machine_int_t py_obj_hash(py_obj_t o_in) {
if (o_in == py_const_false) {
return 0; // needs to hash to same as the integer 0, since False==0
} else if (o_in == py_const_true) {
return 1; // needs to hash to same as the integer 1, since True==1
} else if (IS_SMALL_INT(o_in)) {
return FROM_SMALL_INT(o_in);
} else if (IS_O(o_in, O_CONST)) {
return (machine_int_t)o_in;
} else if (IS_O(o_in, O_STR)) {
return ((py_obj_base_t*)o_in)->u_str;
} else {
......@@ -216,11 +218,32 @@ int py_obj_hash(py_obj_t o_in) {
}
}
// this function implements the '==' operator (and so the inverse of '!=')
// from the python language reference:
// "The objects need not have the same type. If both are numbers, they are converted
// to a common type. Otherwise, the == and != operators always consider objects of
// different types to be unequal."
// note also that False==0 and True==1 are true expressions
bool py_obj_equal(py_obj_t o1, py_obj_t o2) {
if (o1 == o2) {
return true;
} else if (IS_SMALL_INT(o1) && IS_SMALL_INT(o2)) {
return false;
} else if (IS_SMALL_INT(o1) || IS_SMALL_INT(o2)) {
if (IS_SMALL_INT(o1) && IS_SMALL_INT(o2)) {
return false;
} else {
if (IS_SMALL_INT(o2)) {
py_obj_t temp = o1; o1 = o2; o2 = temp;
}
// o1 is the SMALL_INT, o2 is not
py_small_int_t val = FROM_SMALL_INT(o1);
if (o2 == py_const_false) {
return val == 0;
} else if (o2 == py_const_true) {
return val == 1;
} else {
return false;
}
}
} else if (IS_O(o1, O_STR) && IS_O(o2, O_STR)) {
return ((py_obj_base_t*)o1)->u_str == ((py_obj_base_t*)o2)->u_str;
} else {
......@@ -249,7 +272,7 @@ py_map_elem_t* py_map_lookup_helper(py_map_t *map, py_obj_t index, bool add_if_n
py_map_elem_t *old_table = map->table;
map->alloc = get_doubling_prime_greater_or_equal_to(map->alloc + 1);
map->used = 0;
map->table = m_new(py_map_elem_t, map->alloc);
map->table = m_new0(py_map_elem_t, map->alloc);
for (int i = 0; i < old_alloc; i++) {
if (old_table[i].key != NULL) {
py_map_lookup_helper(map, old_table[i].key, true)->value = old_table[i].value;
......@@ -268,9 +291,11 @@ py_map_elem_t* py_map_lookup_helper(py_map_t *map, py_obj_t index, bool add_if_n
}
} else if (elem->key == index || (is_map_py_obj && py_obj_equal(elem->key, index))) {
// found it
/* it seems CPython does not replace the index; try x={True:'true'};x[1]='one';x
if (add_if_not_found) {
elem->key = index;
}
*/
return elem;
} else {
// not yet found, keep searching in this table
......@@ -395,6 +420,7 @@ static qstr q___build_class__;
static qstr q___next__;
static qstr q_AttributeError;
static qstr q_IndexError;
static qstr q_KeyError;
static qstr q_NameError;
static qstr q_TypeError;
......@@ -431,6 +457,14 @@ static py_code_t *unique_codes;
py_obj_t fun_list_append;
py_obj_t fun_gen_instance_next;
py_obj_t py_builtin___repl_print__(py_obj_t o) {
if (o != py_const_none) {
py_obj_print(o);
printf("\n");
}
return py_const_none;
}
py_obj_t py_builtin_print(py_obj_t o) {
if (IS_O(o, O_STR)) {
// special case, print string raw
......@@ -492,6 +526,7 @@ void rt_init() {
q___next__ = qstr_from_str_static("__next__");
q_AttributeError = qstr_from_str_static("AttributeError");
q_IndexError = qstr_from_str_static("IndexError");
q_KeyError = qstr_from_str_static("KeyError");
q_NameError = qstr_from_str_static("NameError");
q_TypeError = qstr_from_str_static("TypeError");
......@@ -505,12 +540,13 @@ void rt_init() {
py_qstr_map_lookup(map_globals, qstr_from_str_static("__name__"), true)->value = py_obj_new_str(qstr_from_str_static("__main__"));
py_map_init(&map_builtins, MAP_QSTR, 3);
py_qstr_map_lookup(&map_builtins, qstr_from_str_static("__repl_print__"), true)->value = rt_make_function_1(py_builtin___repl_print__);
py_qstr_map_lookup(&map_builtins, q_print, true)->value = rt_make_function_1(py_builtin_print);
py_qstr_map_lookup(&map_builtins, q_len, true)->value = rt_make_function_1(py_builtin_len);
py_qstr_map_lookup(&map_builtins, q___build_class__, true)->value = rt_make_function_2(py_builtin___build_class__);
py_qstr_map_lookup(&map_builtins, qstr_from_str_static("range"), true)->value = rt_make_function_1(py_builtin_range);
next_unique_code_id = 1;
next_unique_code_id = 2; // 1 is reserved for the __main__ module scope
unique_codes = NULL;
fun_list_append = rt_make_function_2(rt_list_append);
......@@ -529,8 +565,12 @@ void rt_deinit() {
#endif
}
int rt_get_new_unique_code_id() {
return next_unique_code_id++;
int rt_get_unique_code_id(bool is_main_module) {
if (is_main_module) {
return 1;
} else {
return next_unique_code_id++;
}
}
static void alloc_unique_codes() {
......@@ -896,8 +936,17 @@ py_obj_t rt_binary_op(int op, py_obj_t lhs, py_obj_t rhs) {
DEBUG_OP_printf("binary %d %p %p\n", op, lhs, rhs);
if (op == RT_BINARY_OP_SUBSCR) {
if ((IS_O(lhs, O_TUPLE) || IS_O(lhs, O_LIST))) {
// tuple/list load
uint index = get_index(lhs, rhs);
return ((py_obj_base_t*)lhs)->u_tuple_list.items[index];
} else if (IS_O(lhs, O_MAP)) {
// map load
py_map_elem_t *elem = py_map_lookup(lhs, rhs, false);
if (elem == NULL) {
nlr_jump(py_obj_new_exception_2(q_KeyError, "<value>", NULL, NULL));
} else {
return elem->value;
}
} else {
assert(0);
}
......@@ -1409,16 +1458,16 @@ no_attr:
dest[0] = NULL;
}
void rt_store_attr(py_obj_t base, qstr attr, py_obj_t val) {
DEBUG_OP_printf("store attr %p.%s <- %p\n", base, qstr_str(attr), val);
void rt_store_attr(py_obj_t base, qstr attr, py_obj_t value) {
DEBUG_OP_printf("store attr %p.%s <- %p\n", base, qstr_str(attr), value);
if (IS_O(base, O_OBJ)) {
// logic: look in class locals (no add) then obj members (add) (TODO check this against CPython)
py_obj_base_t *o = base;
py_map_elem_t *elem = py_qstr_map_lookup(o->u_obj.class->u_class.locals, attr, false);
if (elem != NULL) {
elem->value = val;
elem->value = value;
} else {
elem = py_qstr_map_lookup(o->u_obj.class->u_class.locals, attr, true)->value = val;
elem = py_qstr_map_lookup(o->u_obj.class->u_class.locals, attr, true)->value = value;
}
} else {
printf("?AttributeError: '%s' object has no attribute '%s'\n", py_obj_get_type_str(base), qstr_str(attr));
......@@ -1427,6 +1476,7 @@ void rt_store_attr(py_obj_t base, qstr attr, py_obj_t val) {
}
void rt_store_subscr(py_obj_t base, py_obj_t index, py_obj_t value) {
DEBUG_OP_printf("store subscr %p[%p] <- %p\n", base, index, value);
if (IS_O(base, O_LIST)) {
// list store
uint i = get_index(base, index);
......
......@@ -91,7 +91,7 @@ extern py_obj_t py_const_stop_iteration; // special object indicating end of ite
void rt_init();
void rt_deinit();
int rt_get_new_unique_code_id();
int rt_get_unique_code_id(bool is_main_module);
void rt_assign_byte_code(int unique_code_id, byte *code, uint len, int n_args, int n_locals, int n_stack, bool is_generator);
void rt_assign_native_code(int unique_code_id, py_fun_t f, uint len, int n_args);
void rt_assign_inline_asm_code(int unique_code_id, py_fun_t f, uint len, int n_args);
......
......@@ -31,7 +31,7 @@ PY_O = \
vm.o \
OBJ = $(addprefix $(BUILD)/, $(SRC_C:.c=.o) $(PY_O))
LIB =
LIB = -lreadline
PROG = py
$(PROG): $(BUILD) $(OBJ)
......
......@@ -10,61 +10,152 @@
#include "compile.h"
#include "runtime.h"
int main(int argc, char **argv) {
qstr_init();
rt_init();
#include <readline/readline.h>
if (argc != 2) {
printf("usage: py <file>\n");
return 1;
bool str_startswith_word(const char *str, const char *head) {
int i;
for (i = 0; str[i] && head[i]; i++) {
if (str[i] != head[i]) {
return false;
}
}
return head[i] == '\0' && (str[i] == '\0' || !g_unichar_isalpha(str[i]));
}
bool is_compound_stmt(const char *line) {
// TODO also "compound" if unmatched open bracket
return
str_startswith_word(line, "if")
|| str_startswith_word(line, "while")
|| str_startswith_word(line, "for")
|| str_startswith_word(line, "true")
|| str_startswith_word(line, "with")
|| str_startswith_word(line, "def")
|| str_startswith_word(line, "class")
|| str_startswith_word(line, "@");
}
char *str_join(const char *s1, int sep_char, const char *s2) {
int l1 = strlen(s1);
int l2 = strlen(s2);
char *s = m_new(char, l1 + l2 + 2);
memcpy(s, s1, l1);
if (sep_char != 0) {
s[l1] = sep_char;
l1 += 1;
}
memcpy(s + l1, s2, l2);
return s;
}
void do_repl() {
for (;;) {
char *line = readline(">>> ");
if (line == NULL) {
// EOF
return;
}
if (is_compound_stmt(line)) {
for (;;) {
char *line2 = readline("... ");
if (line2 == NULL || strlen(line2) == 0) {
break;
}
char *line3 = str_join(line, '\n', line2);
m_free(line);
m_free(line2);
line = line3;
}
}
py_lexer_t *lex = py_lexer_from_str_len("<stdin>", line, strlen(line), false);
py_parse_node_t pn = py_parse(lex, PY_PARSE_SINGLE_INPUT);
if (pn != PY_PARSE_NODE_NULL) {
//py_parse_node_show(pn, 0);
bool comp_ok = py_compile(pn, true);
if (comp_ok) {
py_obj_t module_fun = rt_make_function_from_id(1);
if (module_fun != py_const_none) {
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
rt_call_function_0(module_fun);
nlr_pop();
} else {
// uncaught exception
py_obj_print((py_obj_t)nlr.ret_val);
printf("\n");
}
}
}
}
}
py_lexer_t *lex = py_lexer_from_file(argv[1]);
}
void do_file(const char *file) {
py_lexer_t *lex = py_lexer_from_file(file);
//const char *pysrc = "def f():\n x=x+1\n print(42)\n";
//py_lexer_t *lex = py_lexer_from_str_len("<>", pysrc, strlen(pysrc), false);
if (lex == NULL) {
return 1;
return;
}
if (0) {
// just tokenise
while (!py_lexer_is_kind(lex, PY_TOKEN_END)) {
py_token_show(py_lexer_cur(lex));
py_lexer_to_next(lex);
}
py_lexer_free(lex);
} else {
py_parse_node_t pn = py_parse(lex, 0);
// compile
py_parse_node_t pn = py_parse(lex, PY_PARSE_FILE_INPUT);
if (pn != PY_PARSE_NODE_NULL) {
//printf("----------------\n");
//parse_node_show(pn, 0);
//printf("----------------\n");
py_compile(pn);
bool comp_ok = py_compile(pn, false);
//printf("----------------\n");
}
}
py_lexer_free(lex);
py_lexer_free(lex);
#if !MICROPY_EMIT_CPYTHON
if (1) {
// execute it
py_obj_t module_fun = rt_make_function_from_id(1);
if (module_fun != py_const_none) {
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
py_obj_t ret = rt_call_function_0(module_fun);
printf("done! got: ");
py_obj_print(ret);
printf("\n");
nlr_pop();
} else {
// uncaught exception
printf("exception: ");
py_obj_print((py_obj_t)nlr.ret_val);
printf("\n");
if (1 && comp_ok) {
// execute it
py_obj_t module_fun = rt_make_function_from_id(1);
if (module_fun != py_const_none) {
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
py_obj_t ret = rt_call_function_0(module_fun);
printf("done! got: ");
py_obj_print(ret);
printf("\n");
nlr_pop();
} else {
// uncaught exception
printf("exception: ");
py_obj_print((py_obj_t)nlr.ret_val);
printf("\n");
}
}
}
#endif
}
}
#endif
}
int main(int argc, char **argv) {
qstr_init();
rt_init();
if (argc == 1) {
do_repl();
} else if (argc == 2) {
do_file(argv[1]);
} else {
printf("usage: py [<file>]\n");
return 1;
}
rt_deinit();
//printf("total bytes = %d\n", m_get_total_bytes_allocated());
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment