Skip to content

Commit 0bfc763

Browse files
committed
py: Protect mp_parse and mp_compile with nlr push/pop block.
To enable parsing constants more efficiently, mp_parse should be allowed to raise an exception, and mp_compile can already raise a MemoryError. So these functions need to be protected by an nlr push/pop block. This patch adds that feature in all places. This allows to simplify how mp_parse and mp_compile are called: they now raise an exception if they have an error and so explicit checking is not needed anymore.
1 parent e1e359f commit 0bfc763

17 files changed

Lines changed: 98 additions & 366 deletions

File tree

bare-arm/main.c

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
#include <string.h>
44

55
#include "py/nlr.h"
6-
#include "py/parsehelper.h"
76
#include "py/compile.h"
87
#include "py/runtime.h"
98
#include "py/repl.h"
@@ -15,29 +14,11 @@ void do_str(const char *src) {
1514
return;
1615
}
1716

18-
mp_parse_error_kind_t parse_error_kind;
19-
mp_parse_node_t pn = mp_parse(lex, MP_PARSE_SINGLE_INPUT, &parse_error_kind);
20-
21-
if (pn == MP_PARSE_NODE_NULL) {
22-
// parse error
23-
mp_parse_show_exception(lex, parse_error_kind);
24-
mp_lexer_free(lex);
25-
return;
26-
}
27-
28-
// parse okay
29-
qstr source_name = lex->source_name;
30-
mp_lexer_free(lex);
31-
mp_obj_t module_fun = mp_compile(pn, source_name, MP_EMIT_OPT_NONE, true);
32-
33-
if (mp_obj_is_exception_instance(module_fun)) {
34-
// compile error
35-
mp_obj_print_exception(printf_wrapper, NULL, module_fun);
36-
return;
37-
}
38-
3917
nlr_buf_t nlr;
4018
if (nlr_push(&nlr) == 0) {
19+
qstr source_name = lex->source_name;
20+
mp_parse_node_t pn = mp_parse(lex, MP_PARSE_SINGLE_INPUT);
21+
mp_obj_t module_fun = mp_compile(pn, source_name, MP_EMIT_OPT_NONE, true);
4122
mp_call_function_0(module_fun);
4223
nlr_pop();
4324
} else {

cc3200/mptask.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
#include "lexer.h"
3737
#include "parse.h"
3838
#include "obj.h"
39-
#include "parsehelper.h"
4039
#include "compile.h"
4140
#include "runtime0.h"
4241
#include "runtime.h"

esp8266/main.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
#include <string.h>
2929

3030
#include "py/nlr.h"
31-
#include "py/parsehelper.h"
3231
#include "py/compile.h"
3332
#include "py/runtime0.h"
3433
#include "py/runtime.h"

minimal/main.c

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
#include <string.h>
44

55
#include "py/nlr.h"
6-
#include "py/parsehelper.h"
76
#include "py/compile.h"
87
#include "py/runtime.h"
98
#include "py/repl.h"
@@ -15,32 +14,15 @@
1514
void do_str(const char *src) {
1615
mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0);
1716
if (lex == NULL) {
18-
return;
19-
}
20-
21-
mp_parse_error_kind_t parse_error_kind;
22-
mp_parse_node_t pn = mp_parse(lex, MP_PARSE_SINGLE_INPUT, &parse_error_kind);
23-
24-
if (pn == MP_PARSE_NODE_NULL) {
25-
// parse error
26-
mp_parse_show_exception(lex, parse_error_kind);
27-
mp_lexer_free(lex);
28-
return;
29-
}
30-
31-
// parse okay
32-
qstr source_name = lex->source_name;
33-
mp_lexer_free(lex);
34-
mp_obj_t module_fun = mp_compile(pn, source_name, MP_EMIT_OPT_NONE, true);
35-
36-
if (mp_obj_is_exception_instance(module_fun)) {
37-
// compile error
38-
mp_obj_print_exception(printf_wrapper, NULL, module_fun);
17+
printf("MemoryError: lexer could not allocate memory\n");
3918
return;
4019
}
4120

4221
nlr_buf_t nlr;
4322
if (nlr_push(&nlr) == 0) {
23+
qstr source_name = lex->source_name;
24+
mp_parse_node_t pn = mp_parse(lex, MP_PARSE_SINGLE_INPUT);
25+
mp_obj_t module_fun = mp_compile(pn, source_name, MP_EMIT_OPT_NONE, true);
4426
mp_call_function_0(module_fun);
4527
nlr_pop();
4628
} else {

py/compile.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3823,7 +3823,7 @@ mp_obj_t mp_compile(mp_parse_node_t pn, qstr source_file, uint emit_opt, bool is
38233823
m_del_obj(compiler_t, comp);
38243824

38253825
if (compile_error != MP_OBJ_NULL) {
3826-
return compile_error;
3826+
nlr_raise(compile_error);
38273827
} else {
38283828
#if MICROPY_EMIT_CPYTHON
38293829
// can't create code, so just return true

py/compile.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ enum {
3939
MP_EMIT_OPT_ASM_THUMB,
4040
};
4141

42+
// the compiler will raise an exception if an error occurred
4243
// the compiler will free the parse tree (pn) before it returns
4344
mp_obj_t mp_compile(mp_parse_node_t pn, qstr source_file, uint emit_opt, bool is_repl);
4445

py/parse.c

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <assert.h>
3131
#include <string.h>
3232

33+
#include "py/nlr.h"
3334
#include "py/lexer.h"
3435
#include "py/parse.h"
3536
#include "py/parsenum.h"
@@ -382,7 +383,7 @@ STATIC void push_result_rule(parser_t *parser, mp_uint_t src_line, const rule_t
382383
push_result_node(parser, (mp_parse_node_t)pn);
383384
}
384385

385-
mp_parse_node_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind, mp_parse_error_kind_t *parse_error_kind_out) {
386+
mp_parse_node_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) {
386387

387388
// initialise parser and allocate memory for its stacks
388389

@@ -717,15 +718,15 @@ mp_parse_node_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind, mp_p
717718
}
718719
}
719720

720-
mp_parse_node_t result;
721+
mp_obj_t exc = MP_OBJ_NULL;
722+
mp_parse_node_t result = MP_PARSE_NODE_NULL;
721723

722724
// check if we had a memory error
723725
if (parser.had_memory_error) {
724726
memory_error:
725-
*parse_error_kind_out = MP_PARSE_ERROR_MEMORY;
726-
result = MP_PARSE_NODE_NULL;
727+
exc = mp_obj_new_exception_msg(&mp_type_MemoryError,
728+
"parser could not allocate enough memory");
727729
goto finished;
728-
729730
}
730731

731732
// check we are at the end of the token stream
@@ -747,17 +748,30 @@ mp_parse_node_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind, mp_p
747748
// free the memory that we don't need anymore
748749
m_del(rule_stack_t, parser.rule_stack, parser.rule_stack_alloc);
749750
m_del(mp_parse_node_t, parser.result_stack, parser.result_stack_alloc);
750-
751-
// return the result
752-
return result;
751+
// we also free the lexer on behalf of the caller (see below)
752+
753+
if (exc != MP_OBJ_NULL) {
754+
// had an error so raise the exception
755+
// add traceback to give info about file name and location
756+
// we don't have a 'block' name, so just pass the NULL qstr to indicate this
757+
mp_obj_exception_add_traceback(exc, lex->source_name, lex->tok_line, MP_QSTR_NULL);
758+
mp_lexer_free(lex);
759+
nlr_raise(exc);
760+
} else {
761+
mp_lexer_free(lex);
762+
return result;
763+
}
753764

754765
syntax_error:
755766
if (lex->tok_kind == MP_TOKEN_INDENT) {
756-
*parse_error_kind_out = MP_PARSE_ERROR_UNEXPECTED_INDENT;
767+
exc = mp_obj_new_exception_msg(&mp_type_IndentationError,
768+
"unexpected indent");
757769
} else if (lex->tok_kind == MP_TOKEN_DEDENT_MISMATCH) {
758-
*parse_error_kind_out = MP_PARSE_ERROR_UNMATCHED_UNINDENT;
770+
exc = mp_obj_new_exception_msg(&mp_type_IndentationError,
771+
"unindent does not match any outer indentation level");
759772
} else {
760-
*parse_error_kind_out = MP_PARSE_ERROR_INVALID_SYNTAX;
773+
exc = mp_obj_new_exception_msg(&mp_type_SyntaxError,
774+
"invalid syntax");
761775
#ifdef USE_RULE_NAME
762776
// debugging: print the rule name that failed and the token
763777
printf("rule: %s\n", rule->rule_name);
@@ -766,6 +780,5 @@ mp_parse_node_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind, mp_p
766780
#endif
767781
#endif
768782
}
769-
result = MP_PARSE_NODE_NULL;
770783
goto finished;
771784
}

py/parse.h

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -90,14 +90,8 @@ typedef enum {
9090
MP_PARSE_EVAL_INPUT,
9191
} mp_parse_input_kind_t;
9292

93-
typedef enum {
94-
MP_PARSE_ERROR_MEMORY,
95-
MP_PARSE_ERROR_UNEXPECTED_INDENT,
96-
MP_PARSE_ERROR_UNMATCHED_UNINDENT,
97-
MP_PARSE_ERROR_INVALID_SYNTAX,
98-
} mp_parse_error_kind_t;
99-
100-
// returns MP_PARSE_NODE_NULL on error, and then parse_error_kind_out is valid
101-
mp_parse_node_t mp_parse(struct _mp_lexer_t *lex, mp_parse_input_kind_t input_kind, mp_parse_error_kind_t *parse_error_kind_out);
93+
// the parser will raise an exception if an error occurred
94+
// the parser will free the lexer before it returns
95+
mp_parse_node_t mp_parse(struct _mp_lexer_t *lex, mp_parse_input_kind_t input_kind);
10296

10397
#endif // __MICROPY_INCLUDED_PY_PARSE_H__

py/parsehelper.c

Lines changed: 0 additions & 87 deletions
This file was deleted.

py/parsehelper.h

Lines changed: 0 additions & 36 deletions
This file was deleted.

0 commit comments

Comments
 (0)