summaryrefslogtreecommitdiffstatshomepage
path: root/py
diff options
context:
space:
mode:
authorDamien George <damien.p.george@gmail.com>2015-02-07 18:33:58 +0000
committerDamien George <damien.p.george@gmail.com>2015-02-07 18:33:58 +0000
commit0bfc7638baa4c5a4a2351364ab770a188dcab302 (patch)
tree0127fcea13a875d37dd9cfa07dc6921bf387c578 /py
parente1e359ff59d6bbf09441cc1f3965be63f1046182 (diff)
downloadmicropython-0bfc7638baa4c5a4a2351364ab770a188dcab302.tar.gz
micropython-0bfc7638baa4c5a4a2351364ab770a188dcab302.zip
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.
Diffstat (limited to 'py')
-rw-r--r--py/compile.c2
-rw-r--r--py/compile.h1
-rw-r--r--py/parse.c37
-rw-r--r--py/parse.h12
-rw-r--r--py/parsehelper.c87
-rw-r--r--py/parsehelper.h36
-rw-r--r--py/py.mk1
-rw-r--r--py/runtime.c54
8 files changed, 48 insertions, 182 deletions
diff --git a/py/compile.c b/py/compile.c
index 8e4723f230..7023e9c403 100644
--- a/py/compile.c
+++ b/py/compile.c
@@ -3823,7 +3823,7 @@ mp_obj_t mp_compile(mp_parse_node_t pn, qstr source_file, uint emit_opt, bool is
m_del_obj(compiler_t, comp);
if (compile_error != MP_OBJ_NULL) {
- return compile_error;
+ nlr_raise(compile_error);
} else {
#if MICROPY_EMIT_CPYTHON
// can't create code, so just return true
diff --git a/py/compile.h b/py/compile.h
index 9d612da532..2d15de8b9a 100644
--- a/py/compile.h
+++ b/py/compile.h
@@ -39,6 +39,7 @@ enum {
MP_EMIT_OPT_ASM_THUMB,
};
+// the compiler will raise an exception if an error occurred
// the compiler will free the parse tree (pn) before it returns
mp_obj_t mp_compile(mp_parse_node_t pn, qstr source_file, uint emit_opt, bool is_repl);
diff --git a/py/parse.c b/py/parse.c
index 881a11e734..569cf257a7 100644
--- a/py/parse.c
+++ b/py/parse.c
@@ -30,6 +30,7 @@
#include <assert.h>
#include <string.h>
+#include "py/nlr.h"
#include "py/lexer.h"
#include "py/parse.h"
#include "py/parsenum.h"
@@ -382,7 +383,7 @@ STATIC void push_result_rule(parser_t *parser, mp_uint_t src_line, const rule_t
push_result_node(parser, (mp_parse_node_t)pn);
}
-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) {
+mp_parse_node_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) {
// initialise parser and allocate memory for its stacks
@@ -717,15 +718,15 @@ mp_parse_node_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind, mp_p
}
}
- mp_parse_node_t result;
+ mp_obj_t exc = MP_OBJ_NULL;
+ mp_parse_node_t result = MP_PARSE_NODE_NULL;
// check if we had a memory error
if (parser.had_memory_error) {
memory_error:
- *parse_error_kind_out = MP_PARSE_ERROR_MEMORY;
- result = MP_PARSE_NODE_NULL;
+ exc = mp_obj_new_exception_msg(&mp_type_MemoryError,
+ "parser could not allocate enough memory");
goto finished;
-
}
// check we are at the end of the token stream
@@ -747,17 +748,30 @@ finished:
// free the memory that we don't need anymore
m_del(rule_stack_t, parser.rule_stack, parser.rule_stack_alloc);
m_del(mp_parse_node_t, parser.result_stack, parser.result_stack_alloc);
-
- // return the result
- return result;
+ // we also free the lexer on behalf of the caller (see below)
+
+ if (exc != MP_OBJ_NULL) {
+ // had an error so raise the exception
+ // add traceback to give info about file name and location
+ // we don't have a 'block' name, so just pass the NULL qstr to indicate this
+ mp_obj_exception_add_traceback(exc, lex->source_name, lex->tok_line, MP_QSTR_NULL);
+ mp_lexer_free(lex);
+ nlr_raise(exc);
+ } else {
+ mp_lexer_free(lex);
+ return result;
+ }
syntax_error:
if (lex->tok_kind == MP_TOKEN_INDENT) {
- *parse_error_kind_out = MP_PARSE_ERROR_UNEXPECTED_INDENT;
+ exc = mp_obj_new_exception_msg(&mp_type_IndentationError,
+ "unexpected indent");
} else if (lex->tok_kind == MP_TOKEN_DEDENT_MISMATCH) {
- *parse_error_kind_out = MP_PARSE_ERROR_UNMATCHED_UNINDENT;
+ exc = mp_obj_new_exception_msg(&mp_type_IndentationError,
+ "unindent does not match any outer indentation level");
} else {
- *parse_error_kind_out = MP_PARSE_ERROR_INVALID_SYNTAX;
+ exc = mp_obj_new_exception_msg(&mp_type_SyntaxError,
+ "invalid syntax");
#ifdef USE_RULE_NAME
// debugging: print the rule name that failed and the token
printf("rule: %s\n", rule->rule_name);
@@ -766,6 +780,5 @@ syntax_error:
#endif
#endif
}
- result = MP_PARSE_NODE_NULL;
goto finished;
}
diff --git a/py/parse.h b/py/parse.h
index 4e7f2b9d19..ee0025a7b2 100644
--- a/py/parse.h
+++ b/py/parse.h
@@ -90,14 +90,8 @@ typedef enum {
MP_PARSE_EVAL_INPUT,
} mp_parse_input_kind_t;
-typedef enum {
- MP_PARSE_ERROR_MEMORY,
- MP_PARSE_ERROR_UNEXPECTED_INDENT,
- MP_PARSE_ERROR_UNMATCHED_UNINDENT,
- MP_PARSE_ERROR_INVALID_SYNTAX,
-} mp_parse_error_kind_t;
-
-// returns MP_PARSE_NODE_NULL on error, and then parse_error_kind_out is valid
-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);
+// the parser will raise an exception if an error occurred
+// the parser will free the lexer before it returns
+mp_parse_node_t mp_parse(struct _mp_lexer_t *lex, mp_parse_input_kind_t input_kind);
#endif // __MICROPY_INCLUDED_PY_PARSE_H__
diff --git a/py/parsehelper.c b/py/parsehelper.c
deleted file mode 100644
index 904268109b..0000000000
--- a/py/parsehelper.c
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * This file is part of the Micro Python project, http://micropython.org/
- *
- * The MIT License (MIT)
- *
- * Copyright (c) 2013, 2014 Damien P. George
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-// these functions are separate from parse.c to keep parser independent of mp_obj_t
-
-#include <stdio.h>
-
-#include "py/parsehelper.h"
-
-#define STR_MEMORY "parser could not allocate enough memory"
-#define STR_UNEXPECTED_INDENT "unexpected indent"
-#define STR_UNMATCHED_UNINDENT "unindent does not match any outer indentation level"
-#define STR_INVALID_SYNTAX "invalid syntax"
-
-void mp_parse_show_exception(mp_lexer_t *lex, mp_parse_error_kind_t parse_error_kind) {
- printf(" File \"%s\", line " UINT_FMT ", column " UINT_FMT "\n", qstr_str(lex->source_name), lex->tok_line, lex->tok_column);
- switch (parse_error_kind) {
- case MP_PARSE_ERROR_MEMORY:
- printf("MemoryError: %s\n", STR_MEMORY);
- break;
-
- case MP_PARSE_ERROR_UNEXPECTED_INDENT:
- printf("IndentationError: %s\n", STR_UNEXPECTED_INDENT);
- break;
-
- case MP_PARSE_ERROR_UNMATCHED_UNINDENT:
- printf("IndentationError: %s\n", STR_UNMATCHED_UNINDENT);
- break;
-
- case MP_PARSE_ERROR_INVALID_SYNTAX:
- default:
- printf("SyntaxError: %s\n", STR_INVALID_SYNTAX);
- break;
- }
-}
-
-mp_obj_t mp_parse_make_exception(mp_lexer_t *lex, mp_parse_error_kind_t parse_error_kind) {
- // make exception object
- mp_obj_t exc;
- switch (parse_error_kind) {
- case MP_PARSE_ERROR_MEMORY:
- exc = mp_obj_new_exception_msg(&mp_type_MemoryError, STR_MEMORY);
- break;
-
- case MP_PARSE_ERROR_UNEXPECTED_INDENT:
- exc = mp_obj_new_exception_msg(&mp_type_IndentationError, STR_UNEXPECTED_INDENT);
- break;
-
- case MP_PARSE_ERROR_UNMATCHED_UNINDENT:
- exc = mp_obj_new_exception_msg(&mp_type_IndentationError, STR_UNMATCHED_UNINDENT);
- break;
-
- case MP_PARSE_ERROR_INVALID_SYNTAX:
- default:
- exc = mp_obj_new_exception_msg(&mp_type_SyntaxError, STR_INVALID_SYNTAX);
- break;
- }
-
- // add traceback to give info about file name and location
- // we don't have a 'block' name, so just pass the NULL qstr to indicate this
- mp_obj_exception_add_traceback(exc, lex->source_name, lex->tok_line, MP_QSTR_NULL);
-
- return exc;
-}
diff --git a/py/parsehelper.h b/py/parsehelper.h
deleted file mode 100644
index 1763809bad..0000000000
--- a/py/parsehelper.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * This file is part of the Micro Python project, http://micropython.org/
- *
- * The MIT License (MIT)
- *
- * Copyright (c) 2013, 2014 Damien P. George
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#ifndef __MICROPY_INCLUDED_PY_PARSEHELPER_H__
-#define __MICROPY_INCLUDED_PY_PARSEHELPER_H__
-
-#include "py/lexer.h"
-#include "py/parse.h"
-#include "py/obj.h"
-
-void mp_parse_show_exception(mp_lexer_t *lex, mp_parse_error_kind_t parse_error_kind);
-mp_obj_t mp_parse_make_exception(mp_lexer_t *lex, mp_parse_error_kind_t parse_error_kind);
-
-#endif // __MICROPY_INCLUDED_PY_PARSEHELPER_H__
diff --git a/py/py.mk b/py/py.mk
index 87e8e7141e..6227ad9eb4 100644
--- a/py/py.mk
+++ b/py/py.mk
@@ -28,7 +28,6 @@ PY_O_BASENAME = \
lexerstr.o \
lexerunix.o \
parse.o \
- parsehelper.o \
scope.o \
compile.o \
emitcommon.o \
diff --git a/py/runtime.c b/py/runtime.c
index fc8d128e77..080d061cd1 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -30,7 +30,6 @@
#include "py/mpstate.h"
#include "py/nlr.h"
-#include "py/parsehelper.h"
#include "py/parsenum.h"
#include "py/compile.h"
#include "py/objtuple.h"
@@ -1228,47 +1227,30 @@ void mp_import_all(mp_obj_t module) {
// this is implemented in this file so it can optimise access to locals/globals
mp_obj_t mp_parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t parse_input_kind, mp_obj_dict_t *globals, mp_obj_dict_t *locals) {
- // parse the string
- mp_parse_error_kind_t parse_error_kind;
- mp_parse_node_t pn = mp_parse(lex, parse_input_kind, &parse_error_kind);
-
- if (pn == MP_PARSE_NODE_NULL) {
- // parse error; raise exception
- mp_obj_t exc = mp_parse_make_exception(lex, parse_error_kind);
- mp_lexer_free(lex);
- nlr_raise(exc);
- }
-
- qstr source_name = lex->source_name;
- mp_lexer_free(lex);
+ // save context
+ mp_obj_dict_t *volatile old_globals = mp_globals_get();
+ mp_obj_dict_t *volatile old_locals = mp_locals_get();
- // save context and set new context
- mp_obj_dict_t *old_globals = mp_globals_get();
- mp_obj_dict_t *old_locals = mp_locals_get();
+ // set new context
mp_globals_set(globals);
mp_locals_set(locals);
- // compile the string
- mp_obj_t module_fun = mp_compile(pn, source_name, MP_EMIT_OPT_NONE, false);
-
- // check if there was a compile error
- if (mp_obj_is_exception_instance(module_fun)) {
- mp_globals_set(old_globals);
- mp_locals_set(old_locals);
- nlr_raise(module_fun);
- }
-
- // for compile only
- if (MICROPY_PY_BUILTINS_COMPILE && globals == NULL) {
- mp_globals_set(old_globals);
- mp_locals_set(old_locals);
- return module_fun;
- }
-
- // complied successfully, execute it
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
- mp_obj_t ret = mp_call_function_0(module_fun);
+ qstr source_name = lex->source_name;
+ mp_parse_node_t pn = mp_parse(lex, parse_input_kind);
+ mp_obj_t module_fun = mp_compile(pn, source_name, MP_EMIT_OPT_NONE, false);
+
+ mp_obj_t ret;
+ if (MICROPY_PY_BUILTINS_COMPILE && globals == NULL) {
+ // for compile only, return value is the module function
+ ret = module_fun;
+ } else {
+ // execute module function and get return value
+ ret = mp_call_function_0(module_fun);
+ }
+
+ // finish nlr block, restore context and return value
nlr_pop();
mp_globals_set(old_globals);
mp_locals_set(old_locals);