Commit 91a19999 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge commit 'a4285' into refcounting

Had to make a decent number of edits to Python-ast.c
parents 20beb619 a4285455
...@@ -789,27 +789,27 @@ static int init_types(void) ...@@ -789,27 +789,27 @@ static int init_types(void)
if (!add_attributes(expr_context_type, NULL, 0)) return 0; if (!add_attributes(expr_context_type, NULL, 0)) return 0;
Load_type = make_type("Load", expr_context_type, NULL, 0); Load_type = make_type("Load", expr_context_type, NULL, 0);
if (!Load_type) return 0; if (!Load_type) return 0;
Load_singleton = PyType_GenericNew(Load_type, NULL, NULL); Load_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(Load_type, NULL, NULL));
if (!Load_singleton) return 0; if (!Load_singleton) return 0;
Store_type = make_type("Store", expr_context_type, NULL, 0); Store_type = make_type("Store", expr_context_type, NULL, 0);
if (!Store_type) return 0; if (!Store_type) return 0;
Store_singleton = PyType_GenericNew(Store_type, NULL, NULL); Store_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(Store_type, NULL, NULL));
if (!Store_singleton) return 0; if (!Store_singleton) return 0;
Del_type = make_type("Del", expr_context_type, NULL, 0); Del_type = make_type("Del", expr_context_type, NULL, 0);
if (!Del_type) return 0; if (!Del_type) return 0;
Del_singleton = PyType_GenericNew(Del_type, NULL, NULL); Del_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(Del_type, NULL, NULL));
if (!Del_singleton) return 0; if (!Del_singleton) return 0;
AugLoad_type = make_type("AugLoad", expr_context_type, NULL, 0); AugLoad_type = make_type("AugLoad", expr_context_type, NULL, 0);
if (!AugLoad_type) return 0; if (!AugLoad_type) return 0;
AugLoad_singleton = PyType_GenericNew(AugLoad_type, NULL, NULL); AugLoad_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(AugLoad_type, NULL, NULL));
if (!AugLoad_singleton) return 0; if (!AugLoad_singleton) return 0;
AugStore_type = make_type("AugStore", expr_context_type, NULL, 0); AugStore_type = make_type("AugStore", expr_context_type, NULL, 0);
if (!AugStore_type) return 0; if (!AugStore_type) return 0;
AugStore_singleton = PyType_GenericNew(AugStore_type, NULL, NULL); AugStore_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(AugStore_type, NULL, NULL));
if (!AugStore_singleton) return 0; if (!AugStore_singleton) return 0;
Param_type = make_type("Param", expr_context_type, NULL, 0); Param_type = make_type("Param", expr_context_type, NULL, 0);
if (!Param_type) return 0; if (!Param_type) return 0;
Param_singleton = PyType_GenericNew(Param_type, NULL, NULL); Param_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(Param_type, NULL, NULL));
if (!Param_singleton) return 0; if (!Param_singleton) return 0;
slice_type = make_type("slice", &AST_type, NULL, 0); slice_type = make_type("slice", &AST_type, NULL, 0);
if (!slice_type) return 0; if (!slice_type) return 0;
...@@ -827,124 +827,124 @@ static int init_types(void) ...@@ -827,124 +827,124 @@ static int init_types(void)
if (!add_attributes(boolop_type, NULL, 0)) return 0; if (!add_attributes(boolop_type, NULL, 0)) return 0;
And_type = make_type("And", boolop_type, NULL, 0); And_type = make_type("And", boolop_type, NULL, 0);
if (!And_type) return 0; if (!And_type) return 0;
And_singleton = PyType_GenericNew(And_type, NULL, NULL); And_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(And_type, NULL, NULL));
if (!And_singleton) return 0; if (!And_singleton) return 0;
Or_type = make_type("Or", boolop_type, NULL, 0); Or_type = make_type("Or", boolop_type, NULL, 0);
if (!Or_type) return 0; if (!Or_type) return 0;
Or_singleton = PyType_GenericNew(Or_type, NULL, NULL); Or_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(Or_type, NULL, NULL));
if (!Or_singleton) return 0; if (!Or_singleton) return 0;
operator_type = make_type("operator", &AST_type, NULL, 0); operator_type = make_type("operator", &AST_type, NULL, 0);
if (!operator_type) return 0; if (!operator_type) return 0;
if (!add_attributes(operator_type, NULL, 0)) return 0; if (!add_attributes(operator_type, NULL, 0)) return 0;
Add_type = make_type("Add", operator_type, NULL, 0); Add_type = make_type("Add", operator_type, NULL, 0);
if (!Add_type) return 0; if (!Add_type) return 0;
Add_singleton = PyType_GenericNew(Add_type, NULL, NULL); Add_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(Add_type, NULL, NULL));
if (!Add_singleton) return 0; if (!Add_singleton) return 0;
Sub_type = make_type("Sub", operator_type, NULL, 0); Sub_type = make_type("Sub", operator_type, NULL, 0);
if (!Sub_type) return 0; if (!Sub_type) return 0;
Sub_singleton = PyType_GenericNew(Sub_type, NULL, NULL); Sub_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(Sub_type, NULL, NULL));
if (!Sub_singleton) return 0; if (!Sub_singleton) return 0;
Mult_type = make_type("Mult", operator_type, NULL, 0); Mult_type = make_type("Mult", operator_type, NULL, 0);
if (!Mult_type) return 0; if (!Mult_type) return 0;
Mult_singleton = PyType_GenericNew(Mult_type, NULL, NULL); Mult_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(Mult_type, NULL, NULL));
if (!Mult_singleton) return 0; if (!Mult_singleton) return 0;
Div_type = make_type("Div", operator_type, NULL, 0); Div_type = make_type("Div", operator_type, NULL, 0);
if (!Div_type) return 0; if (!Div_type) return 0;
Div_singleton = PyType_GenericNew(Div_type, NULL, NULL); Div_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(Div_type, NULL, NULL));
if (!Div_singleton) return 0; if (!Div_singleton) return 0;
Mod_type = make_type("Mod", operator_type, NULL, 0); Mod_type = make_type("Mod", operator_type, NULL, 0);
if (!Mod_type) return 0; if (!Mod_type) return 0;
Mod_singleton = PyType_GenericNew(Mod_type, NULL, NULL); Mod_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(Mod_type, NULL, NULL));
if (!Mod_singleton) return 0; if (!Mod_singleton) return 0;
Pow_type = make_type("Pow", operator_type, NULL, 0); Pow_type = make_type("Pow", operator_type, NULL, 0);
if (!Pow_type) return 0; if (!Pow_type) return 0;
Pow_singleton = PyType_GenericNew(Pow_type, NULL, NULL); Pow_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(Pow_type, NULL, NULL));
if (!Pow_singleton) return 0; if (!Pow_singleton) return 0;
LShift_type = make_type("LShift", operator_type, NULL, 0); LShift_type = make_type("LShift", operator_type, NULL, 0);
if (!LShift_type) return 0; if (!LShift_type) return 0;
LShift_singleton = PyType_GenericNew(LShift_type, NULL, NULL); LShift_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(LShift_type, NULL, NULL));
if (!LShift_singleton) return 0; if (!LShift_singleton) return 0;
RShift_type = make_type("RShift", operator_type, NULL, 0); RShift_type = make_type("RShift", operator_type, NULL, 0);
if (!RShift_type) return 0; if (!RShift_type) return 0;
RShift_singleton = PyType_GenericNew(RShift_type, NULL, NULL); RShift_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(RShift_type, NULL, NULL));
if (!RShift_singleton) return 0; if (!RShift_singleton) return 0;
BitOr_type = make_type("BitOr", operator_type, NULL, 0); BitOr_type = make_type("BitOr", operator_type, NULL, 0);
if (!BitOr_type) return 0; if (!BitOr_type) return 0;
BitOr_singleton = PyType_GenericNew(BitOr_type, NULL, NULL); BitOr_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(BitOr_type, NULL, NULL));
if (!BitOr_singleton) return 0; if (!BitOr_singleton) return 0;
BitXor_type = make_type("BitXor", operator_type, NULL, 0); BitXor_type = make_type("BitXor", operator_type, NULL, 0);
if (!BitXor_type) return 0; if (!BitXor_type) return 0;
BitXor_singleton = PyType_GenericNew(BitXor_type, NULL, NULL); BitXor_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(BitXor_type, NULL, NULL));
if (!BitXor_singleton) return 0; if (!BitXor_singleton) return 0;
BitAnd_type = make_type("BitAnd", operator_type, NULL, 0); BitAnd_type = make_type("BitAnd", operator_type, NULL, 0);
if (!BitAnd_type) return 0; if (!BitAnd_type) return 0;
BitAnd_singleton = PyType_GenericNew(BitAnd_type, NULL, NULL); BitAnd_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(BitAnd_type, NULL, NULL));
if (!BitAnd_singleton) return 0; if (!BitAnd_singleton) return 0;
FloorDiv_type = make_type("FloorDiv", operator_type, NULL, 0); FloorDiv_type = make_type("FloorDiv", operator_type, NULL, 0);
if (!FloorDiv_type) return 0; if (!FloorDiv_type) return 0;
FloorDiv_singleton = PyType_GenericNew(FloorDiv_type, NULL, NULL); FloorDiv_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(FloorDiv_type, NULL, NULL));
if (!FloorDiv_singleton) return 0; if (!FloorDiv_singleton) return 0;
unaryop_type = make_type("unaryop", &AST_type, NULL, 0); unaryop_type = make_type("unaryop", &AST_type, NULL, 0);
if (!unaryop_type) return 0; if (!unaryop_type) return 0;
if (!add_attributes(unaryop_type, NULL, 0)) return 0; if (!add_attributes(unaryop_type, NULL, 0)) return 0;
Invert_type = make_type("Invert", unaryop_type, NULL, 0); Invert_type = make_type("Invert", unaryop_type, NULL, 0);
if (!Invert_type) return 0; if (!Invert_type) return 0;
Invert_singleton = PyType_GenericNew(Invert_type, NULL, NULL); Invert_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(Invert_type, NULL, NULL));
if (!Invert_singleton) return 0; if (!Invert_singleton) return 0;
Not_type = make_type("Not", unaryop_type, NULL, 0); Not_type = make_type("Not", unaryop_type, NULL, 0);
if (!Not_type) return 0; if (!Not_type) return 0;
Not_singleton = PyType_GenericNew(Not_type, NULL, NULL); Not_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(Not_type, NULL, NULL));
if (!Not_singleton) return 0; if (!Not_singleton) return 0;
UAdd_type = make_type("UAdd", unaryop_type, NULL, 0); UAdd_type = make_type("UAdd", unaryop_type, NULL, 0);
if (!UAdd_type) return 0; if (!UAdd_type) return 0;
UAdd_singleton = PyType_GenericNew(UAdd_type, NULL, NULL); UAdd_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(UAdd_type, NULL, NULL));
if (!UAdd_singleton) return 0; if (!UAdd_singleton) return 0;
USub_type = make_type("USub", unaryop_type, NULL, 0); USub_type = make_type("USub", unaryop_type, NULL, 0);
if (!USub_type) return 0; if (!USub_type) return 0;
USub_singleton = PyType_GenericNew(USub_type, NULL, NULL); USub_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(USub_type, NULL, NULL));
if (!USub_singleton) return 0; if (!USub_singleton) return 0;
cmpop_type = make_type("cmpop", &AST_type, NULL, 0); cmpop_type = make_type("cmpop", &AST_type, NULL, 0);
if (!cmpop_type) return 0; if (!cmpop_type) return 0;
if (!add_attributes(cmpop_type, NULL, 0)) return 0; if (!add_attributes(cmpop_type, NULL, 0)) return 0;
Eq_type = make_type("Eq", cmpop_type, NULL, 0); Eq_type = make_type("Eq", cmpop_type, NULL, 0);
if (!Eq_type) return 0; if (!Eq_type) return 0;
Eq_singleton = PyType_GenericNew(Eq_type, NULL, NULL); Eq_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(Eq_type, NULL, NULL));
if (!Eq_singleton) return 0; if (!Eq_singleton) return 0;
NotEq_type = make_type("NotEq", cmpop_type, NULL, 0); NotEq_type = make_type("NotEq", cmpop_type, NULL, 0);
if (!NotEq_type) return 0; if (!NotEq_type) return 0;
NotEq_singleton = PyType_GenericNew(NotEq_type, NULL, NULL); NotEq_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(NotEq_type, NULL, NULL));
if (!NotEq_singleton) return 0; if (!NotEq_singleton) return 0;
Lt_type = make_type("Lt", cmpop_type, NULL, 0); Lt_type = make_type("Lt", cmpop_type, NULL, 0);
if (!Lt_type) return 0; if (!Lt_type) return 0;
Lt_singleton = PyType_GenericNew(Lt_type, NULL, NULL); Lt_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(Lt_type, NULL, NULL));
if (!Lt_singleton) return 0; if (!Lt_singleton) return 0;
LtE_type = make_type("LtE", cmpop_type, NULL, 0); LtE_type = make_type("LtE", cmpop_type, NULL, 0);
if (!LtE_type) return 0; if (!LtE_type) return 0;
LtE_singleton = PyType_GenericNew(LtE_type, NULL, NULL); LtE_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(LtE_type, NULL, NULL));
if (!LtE_singleton) return 0; if (!LtE_singleton) return 0;
Gt_type = make_type("Gt", cmpop_type, NULL, 0); Gt_type = make_type("Gt", cmpop_type, NULL, 0);
if (!Gt_type) return 0; if (!Gt_type) return 0;
Gt_singleton = PyType_GenericNew(Gt_type, NULL, NULL); Gt_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(Gt_type, NULL, NULL));
if (!Gt_singleton) return 0; if (!Gt_singleton) return 0;
GtE_type = make_type("GtE", cmpop_type, NULL, 0); GtE_type = make_type("GtE", cmpop_type, NULL, 0);
if (!GtE_type) return 0; if (!GtE_type) return 0;
GtE_singleton = PyType_GenericNew(GtE_type, NULL, NULL); GtE_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(GtE_type, NULL, NULL));
if (!GtE_singleton) return 0; if (!GtE_singleton) return 0;
Is_type = make_type("Is", cmpop_type, NULL, 0); Is_type = make_type("Is", cmpop_type, NULL, 0);
if (!Is_type) return 0; if (!Is_type) return 0;
Is_singleton = PyType_GenericNew(Is_type, NULL, NULL); Is_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(Is_type, NULL, NULL));
if (!Is_singleton) return 0; if (!Is_singleton) return 0;
IsNot_type = make_type("IsNot", cmpop_type, NULL, 0); IsNot_type = make_type("IsNot", cmpop_type, NULL, 0);
if (!IsNot_type) return 0; if (!IsNot_type) return 0;
IsNot_singleton = PyType_GenericNew(IsNot_type, NULL, NULL); IsNot_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(IsNot_type, NULL, NULL));
if (!IsNot_singleton) return 0; if (!IsNot_singleton) return 0;
In_type = make_type("In", cmpop_type, NULL, 0); In_type = make_type("In", cmpop_type, NULL, 0);
if (!In_type) return 0; if (!In_type) return 0;
In_singleton = PyType_GenericNew(In_type, NULL, NULL); In_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(In_type, NULL, NULL));
if (!In_singleton) return 0; if (!In_singleton) return 0;
NotIn_type = make_type("NotIn", cmpop_type, NULL, 0); NotIn_type = make_type("NotIn", cmpop_type, NULL, 0);
if (!NotIn_type) return 0; if (!NotIn_type) return 0;
NotIn_singleton = PyType_GenericNew(NotIn_type, NULL, NULL); NotIn_singleton = PyGC_RegisterStaticConstant(PyType_GenericNew(NotIn_type, NULL, NULL));
if (!NotIn_singleton) return 0; if (!NotIn_singleton) return 0;
comprehension_type = make_type("comprehension", &AST_type, comprehension_type = make_type("comprehension", &AST_type,
comprehension_fields, 3); comprehension_fields, 3);
...@@ -6779,13 +6779,10 @@ mod_ty PyAST_obj2mod(PyObject* ast, PyArena* arena, int mode) ...@@ -6779,13 +6779,10 @@ mod_ty PyAST_obj2mod(PyObject* ast, PyArena* arena, int mode)
return res; return res;
} }
// Pyston temporary change: we're not using this file for the python module, at least not yet.
#if 0
int PyAST_Check(PyObject* obj) int PyAST_Check(PyObject* obj)
{ {
init_types(); init_types();
return PyObject_IsInstance(obj, (PyObject*)&AST_type); return PyObject_IsInstance(obj, (PyObject*)&AST_type);
} }
#endif
...@@ -75,7 +75,6 @@ add_library(PYSTON_OBJECTS OBJECT ${OPTIONAL_SRCS} ...@@ -75,7 +75,6 @@ add_library(PYSTON_OBJECTS OBJECT ${OPTIONAL_SRCS}
core/util.cpp core/util.cpp
deadlock_debug_helper.cpp deadlock_debug_helper.cpp
runtime/bool.cpp runtime/bool.cpp
runtime/builtin_modules/ast.cpp
runtime/builtin_modules/builtins.cpp runtime/builtin_modules/builtins.cpp
runtime/builtin_modules/pyston.cpp runtime/builtin_modules/pyston.cpp
runtime/builtin_modules/sys.cpp runtime/builtin_modules/sys.cpp
......
...@@ -524,7 +524,7 @@ extern "C" int PyObject_SetAttr(PyObject* obj, PyObject* name, PyObject* value) ...@@ -524,7 +524,7 @@ extern "C" int PyObject_SetAttr(PyObject* obj, PyObject* name, PyObject* value)
extern "C" int PyObject_SetAttrString(PyObject* v, const char* name, PyObject* w) noexcept { extern "C" int PyObject_SetAttrString(PyObject* v, const char* name, PyObject* w) noexcept {
try { try {
setattr(v, internStringMortal(name), w); setattr(v, autoDecref(internStringMortal(name)), incref(w));
} catch (ExcInfo e) { } catch (ExcInfo e) {
setCAPIException(e); setCAPIException(e);
return -1; return -1;
......
...@@ -651,14 +651,27 @@ public: ...@@ -651,14 +651,27 @@ public:
return r; return r;
} }
AST_Module* convert(mod_ty mod) { AST* convert(mod_ty mod) {
switch (mod->kind) { switch (mod->kind) {
case Module_kind: case Module_kind: {
AST_Module* rtn = new AST_Module(llvm::make_unique<InternedStringPool>());
assert(!this->pool);
this->pool = rtn->interned_strings.get();
convertAll<stmt_ty>(mod->v.Module.body, rtn->body);
return rtn;
}
case Interactive_kind: { case Interactive_kind: {
AST_Module* rtn = new AST_Module(llvm::make_unique<InternedStringPool>()); AST_Module* rtn = new AST_Module(llvm::make_unique<InternedStringPool>());
assert(!this->pool); assert(!this->pool);
this->pool = rtn->interned_strings.get(); this->pool = rtn->interned_strings.get();
convertAll<stmt_ty>(mod->v.Interactive.body, rtn->body); convertAll<stmt_ty>(mod->v.Interactive.body, rtn->body);
makeModuleInteractive(rtn);
return rtn;
}
case Expression_kind: {
AST_Expression* rtn = new AST_Expression(llvm::make_unique<InternedStringPool>());
this->pool = rtn->interned_strings.get();
rtn->body = this->convert(mod->v.Expression.body);
return rtn; return rtn;
} }
default: default:
...@@ -667,7 +680,7 @@ public: ...@@ -667,7 +680,7 @@ public:
} }
}; };
AST_Module* cpythonToPystonAST(mod_ty mod, llvm::StringRef fn) { AST* cpythonToPystonAST(mod_ty mod, llvm::StringRef fn) {
Converter c(fn); Converter c(fn);
return c.convert(mod); return c.convert(mod);
} }
......
...@@ -25,7 +25,7 @@ namespace pyston { ...@@ -25,7 +25,7 @@ namespace pyston {
// Convert a CPython ast object to a Pyston ast object. // Convert a CPython ast object to a Pyston ast object.
// This will also check for certain kinds of "syntax errors" (ex continue not in loop) and will // This will also check for certain kinds of "syntax errors" (ex continue not in loop) and will
// throw them as C++ exceptions. // throw them as C++ exceptions.
AST_Module* cpythonToPystonAST(mod_ty mod, llvm::StringRef fn); AST* cpythonToPystonAST(mod_ty mod, llvm::StringRef fn);
} }
#endif #endif
...@@ -495,26 +495,57 @@ Box* compile(Box* source, Box* fn, Box* type, Box** _args) { ...@@ -495,26 +495,57 @@ Box* compile(Box* source, Box* fn, Box* type, Box** _args) {
RELEASE_ASSERT(iflags == 0, ""); RELEASE_ASSERT(iflags == 0, "");
AST* parsed; AST* parsed;
mod_ty mod;
if (PyAST_Check(source)) { if (PyAST_Check(source)) {
parsed = unboxAst(source); int mode;
ArenaWrapper arena;
if (type_str->s() == "exec")
mode = 0;
else if (type_str->s() == "eval")
mode = 1;
else if (type_str->s() == "single")
mode = 2;
else {
raiseExcHelper(ValueError, "compile() arg 3 must be 'exec', 'eval' or 'single'");
}
mod = PyAST_obj2mod(source, arena, mode);
if (PyErr_Occurred())
throwCAPIException();
parsed = cpythonToPystonAST(mod, filename_str->c_str());
} else { } else {
RELEASE_ASSERT(PyString_Check(source), ""); RELEASE_ASSERT(PyString_Check(source), "");
llvm::StringRef source_str = static_cast<BoxedString*>(source)->s(); int mode;
if (type_str->s() == "exec")
if (type_str->s() == "exec") { mode = Py_file_input;
parsed = parseExec(source_str, future_flags); else if (type_str->s() == "eval")
} else if (type_str->s() == "eval") { mode = Py_eval_input;
parsed = parseEval(source_str, future_flags); else if (type_str->s() == "single")
} else if (type_str->s() == "single") { mode = Py_single_input;
parsed = parseExec(source_str, future_flags, true); else {
} else {
raiseExcHelper(ValueError, "compile() arg 3 must be 'exec', 'eval' or 'single'"); raiseExcHelper(ValueError, "compile() arg 3 must be 'exec', 'eval' or 'single'");
} }
PyCompilerFlags cf;
cf.cf_flags = future_flags;
ArenaWrapper arena;
const char* code = static_cast<BoxedString*>(source)->s().data();
assert(arena);
const char* fn = filename_str->c_str();
mod = PyParser_ASTFromString(code, fn, mode, &cf, arena);
if (!mod)
throwCAPIException();
parsed = cpythonToPystonAST(mod, filename_str->c_str());
} }
if (only_ast) if (only_ast) {
return boxAst(parsed); Box* result = PyAST_mod2obj(mod);
if (PyErr_Occurred())
throwCAPIException();
return result;
}
PyCompilerFlags pcf; PyCompilerFlags pcf;
pcf.cf_flags = future_flags; pcf.cf_flags = future_flags;
...@@ -522,12 +553,14 @@ Box* compile(Box* source, Box* fn, Box* type, Box** _args) { ...@@ -522,12 +553,14 @@ Box* compile(Box* source, Box* fn, Box* type, Box** _args) {
FunctionMetadata* md; FunctionMetadata* md;
if (type_str->s() == "exec" || type_str->s() == "single") { if (type_str->s() == "exec" || type_str->s() == "single") {
// TODO: CPython parses execs as Modules // TODO: CPython parses execs as Modules
if (parsed->type != AST_TYPE::Module) if (parsed->type != AST_TYPE::Module) {
raiseExcHelper(TypeError, "expected Module node, got %s", boxAst(parsed)->cls->tp_name); raiseExcHelper(TypeError, "expected Module node, got %s", AST_TYPE::stringify(parsed->type));
}
md = compileExec(static_cast<AST_Module*>(parsed), filename_str, &pcf); md = compileExec(static_cast<AST_Module*>(parsed), filename_str, &pcf);
} else if (type_str->s() == "eval") { } else if (type_str->s() == "eval") {
if (parsed->type != AST_TYPE::Expression) if (parsed->type != AST_TYPE::Expression) {
raiseExcHelper(TypeError, "expected Expression node, got %s", boxAst(parsed)->cls->tp_name); raiseExcHelper(TypeError, "expected Expression node, got %s", AST_TYPE::stringify(parsed->type));
}
md = compileEval(static_cast<AST_Expression*>(parsed), filename_str, &pcf); md = compileEval(static_cast<AST_Expression*>(parsed), filename_str, &pcf);
} else { } else {
raiseExcHelper(ValueError, "compile() arg 3 must be 'exec', 'eval' or 'single'"); raiseExcHelper(ValueError, "compile() arg 3 must be 'exec', 'eval' or 'single'");
......
...@@ -1027,7 +1027,8 @@ AST_Module* parse_string(const char* code, FutureFlags inherited_flags) { ...@@ -1027,7 +1027,8 @@ AST_Module* parse_string(const char* code, FutureFlags inherited_flags) {
mod_ty mod = PyParser_ASTFromString(code, fn, Py_file_input, &cf, arena); mod_ty mod = PyParser_ASTFromString(code, fn, Py_file_input, &cf, arena);
if (!mod) if (!mod)
throwCAPIException(); throwCAPIException();
auto rtn = cpythonToPystonAST(mod, fn); assert(mod->kind != Interactive_kind);
auto rtn = static_cast<AST_Module*>(cpythonToPystonAST(mod, fn));
return rtn; return rtn;
} }
...@@ -1072,7 +1073,8 @@ AST_Module* parse_file(const char* fn, FutureFlags inherited_flags) { ...@@ -1072,7 +1073,8 @@ AST_Module* parse_file(const char* fn, FutureFlags inherited_flags) {
mod_ty mod = PyParser_ASTFromFile(fp, fn, Py_file_input, 0, 0, &cf, NULL, arena); mod_ty mod = PyParser_ASTFromFile(fp, fn, Py_file_input, 0, 0, &cf, NULL, arena);
if (!mod) if (!mod)
throwCAPIException(); throwCAPIException();
auto rtn = cpythonToPystonAST(mod, fn); assert(mod->kind != Interactive_kind);
auto rtn = static_cast<AST_Module*>(cpythonToPystonAST(mod, fn));
return rtn; return rtn;
} }
...@@ -1158,7 +1160,8 @@ static std::vector<char> _reparse(const char* fn, const std::string& cache_fn, A ...@@ -1158,7 +1160,8 @@ static std::vector<char> _reparse(const char* fn, const std::string& cache_fn, A
mod_ty mod = PyParser_ASTFromFile(fp, fn, Py_file_input, 0, 0, &cf, NULL, arena); mod_ty mod = PyParser_ASTFromFile(fp, fn, Py_file_input, 0, 0, &cf, NULL, arena);
if (!mod) if (!mod)
throwCAPIException(); throwCAPIException();
module = cpythonToPystonAST(mod, fn); assert(mod->kind != Interactive_kind);
module = static_cast<AST_Module*>(cpythonToPystonAST(mod, fn));
} else { } else {
module = pypa_parse(fn, inherited_flags); module = pypa_parse(fn, inherited_flags);
RELEASE_ASSERT(module, "unknown parse error"); RELEASE_ASSERT(module, "unknown parse error");
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <cassert> #include <cassert>
#include <cstdlib> #include <cstdlib>
#include <map>
#include <stdint.h> #include <stdint.h>
#include <string> #include <string>
#include <vector> #include <vector>
...@@ -33,112 +34,132 @@ namespace pyston { ...@@ -33,112 +34,132 @@ namespace pyston {
namespace AST_TYPE { namespace AST_TYPE {
// These are in a pretty random order (started off alphabetical but then I had to add more). // These are in a pretty random order (started off alphabetical but then I had to add more).
// These can be changed freely as long as parse_ast.py is also updated // These can be changed freely as long as parse_ast.py is also updated
#define FOREACH_TYPE(X) \
X(alias, 1) \
X(arguments, 2) \
X(Assert, 3) \
X(Assign, 4) \
X(Attribute, 5) \
X(AugAssign, 6) \
X(BinOp, 7) \
X(BoolOp, 8) \
X(Call, 9) \
X(ClassDef, 10) \
X(Compare, 11) \
X(comprehension, 12) \
X(Delete, 13) \
X(Dict, 14) \
X(Exec, 16) \
X(ExceptHandler, 17) \
X(ExtSlice, 18) \
X(Expr, 19) \
X(For, 20) \
X(FunctionDef, 21) \
X(GeneratorExp, 22) \
X(Global, 23) \
X(If, 24) \
X(IfExp, 25) \
X(Import, 26) \
X(ImportFrom, 27) \
X(Index, 28) \
X(keyword, 29) \
X(Lambda, 30) \
X(List, 31) \
X(ListComp, 32) \
X(Module, 33) \
X(Num, 34) \
X(Name, 35) \
X(Pass, 37) \
X(Pow, 38) \
X(Print, 39) \
X(Raise, 40) \
X(Repr, 41) \
X(Return, 42) \
X(Slice, 44) \
X(Str, 45) \
X(Subscript, 46) \
X(TryExcept, 47) \
X(TryFinally, 48) \
X(Tuple, 49) \
X(UnaryOp, 50) \
X(With, 51) \
X(While, 52) \
X(Yield, 53) \
X(Store, 54) \
X(Load, 55) \
X(Param, 56) \
X(Not, 57) \
X(In, 58) \
X(Is, 59) \
X(IsNot, 60) \
X(Or, 61) \
X(And, 62) \
X(Eq, 63) \
X(NotEq, 64) \
X(NotIn, 65) \
X(GtE, 66) \
X(Gt, 67) \
X(Mod, 68) \
X(Add, 69) \
X(Continue, 70) \
X(Lt, 71) \
X(LtE, 72) \
X(Break, 73) \
X(Sub, 74) \
X(Del, 75) \
X(Mult, 76) \
X(Div, 77) \
X(USub, 78) \
X(BitAnd, 79) \
X(BitOr, 80) \
X(BitXor, 81) \
X(RShift, 82) \
X(LShift, 83) \
X(Invert, 84) \
X(UAdd, 85) \
X(FloorDiv, 86) \
X(DictComp, 15) \
X(Set, 43) \
X(Ellipsis, 87) \
/* like Module, but used for eval. */ \
X(Expression, 88) \
X(SetComp, 89) \
X(Suite, 90) \
\
/* Pseudo-nodes that are specific to this compiler: */ \
X(Branch, 200) \
X(Jump, 201) \
X(ClsAttribute, 202) \
X(AugBinOp, 203) \
X(Invoke, 204) \
X(LangPrimitive, 205) \
/* wraps a ClassDef to make it an expr */ \
X(MakeClass, 206) \
/* wraps a FunctionDef to make it an expr */ \
X(MakeFunction, 207) \
\
/* These aren't real AST types, but since we use AST types to represent binexp types */ \
/* and divmod+truediv are essentially types of binops, we add them here (at least for now): */ \
X(DivMod, 250) \
X(TrueDiv, 251) \
#define GENERATE_ENUM(ENUM, N) ENUM = N,
#define GENERATE_STRING(STRING, N) m[N] = #STRING;
enum AST_TYPE { enum AST_TYPE {
alias = 1, FOREACH_TYPE(GENERATE_ENUM)
arguments = 2,
Assert = 3,
Assign = 4,
Attribute = 5,
AugAssign = 6,
BinOp = 7,
BoolOp = 8,
Call = 9,
ClassDef = 10,
Compare = 11,
comprehension = 12,
Delete = 13,
Dict = 14,
Exec = 16,
ExceptHandler = 17,
ExtSlice = 18,
Expr = 19,
For = 20,
FunctionDef = 21,
GeneratorExp = 22,
Global = 23,
If = 24,
IfExp = 25,
Import = 26,
ImportFrom = 27,
Index = 28,
keyword = 29,
Lambda = 30,
List = 31,
ListComp = 32,
Module = 33,
Num = 34,
Name = 35,
Pass = 37,
Pow = 38,
Print = 39,
Raise = 40,
Repr = 41,
Return = 42,
Slice = 44,
Str = 45,
Subscript = 46,
TryExcept = 47,
TryFinally = 48,
Tuple = 49,
UnaryOp = 50,
With = 51,
While = 52,
Yield = 53,
Store = 54,
Load = 55,
Param = 56,
Not = 57,
In = 58,
Is = 59,
IsNot = 60,
Or = 61,
And = 62,
Eq = 63,
NotEq = 64,
NotIn = 65,
GtE = 66,
Gt = 67,
Mod = 68,
Add = 69,
Continue = 70,
Lt = 71,
LtE = 72,
Break = 73,
Sub = 74,
Del = 75,
Mult = 76,
Div = 77,
USub = 78,
BitAnd = 79,
BitOr = 80,
BitXor = 81,
RShift = 82,
LShift = 83,
Invert = 84,
UAdd = 85,
FloorDiv = 86,
DictComp = 15,
Set = 43,
Ellipsis = 87,
Expression = 88, // like Module, but used for eval.
SetComp = 89,
Suite = 90,
// Pseudo-nodes that are specific to this compiler:
Branch = 200,
Jump = 201,
ClsAttribute = 202,
AugBinOp = 203,
Invoke = 204,
LangPrimitive = 205,
MakeClass = 206, // wraps a ClassDef to make it an expr
MakeFunction = 207, // wraps a FunctionDef to make it an expr
// These aren't real AST types, but since we use AST types to represent binexp types
// and divmod+truediv are essentially types of binops, we add them here (at least for now):
DivMod = 250,
TrueDiv = 251,
}; };
static const char *stringify(int n) {
static std::map<int, const char*> m;
FOREACH_TYPE(GENERATE_STRING)
return m[n];
}
#undef FOREACH_TYPE
#undef GENERATE_ENUM
#undef GENERATE_STRING
}; };
class ASTVisitor; class ASTVisitor;
......
...@@ -256,7 +256,7 @@ private: ...@@ -256,7 +256,7 @@ private:
curblock = NULL; curblock = NULL;
} }
void doContinue() { void doContinue(AST* value) {
assert(curblock); assert(curblock);
for (auto& cont : llvm::make_range(continuations.rbegin(), continuations.rend())) { for (auto& cont : llvm::make_range(continuations.rbegin(), continuations.rend())) {
if (cont.continue_dest) { if (cont.continue_dest) {
...@@ -270,10 +270,10 @@ private: ...@@ -270,10 +270,10 @@ private:
} }
} }
raiseExcHelper(SyntaxError, "'continue' not properly in loop"); raiseSyntaxError("'continue' not properly in loop", value->lineno, value->col_offset, source->getFn()->s(), "", true);
} }
void doBreak() { void doBreak(AST* value) {
assert(curblock); assert(curblock);
for (auto& cont : llvm::make_range(continuations.rbegin(), continuations.rend())) { for (auto& cont : llvm::make_range(continuations.rbegin(), continuations.rend())) {
if (cont.break_dest) { if (cont.break_dest) {
...@@ -287,7 +287,7 @@ private: ...@@ -287,7 +287,7 @@ private:
} }
} }
raiseExcHelper(SyntaxError, "'break' outside loop"); raiseSyntaxError("'break' outside loop", value->lineno, value->col_offset, source->getFn()->s(), "", true);
} }
AST_expr* callNonzero(AST_expr* e) { AST_expr* callNonzero(AST_expr* e) {
...@@ -1322,10 +1322,10 @@ private: ...@@ -1322,10 +1322,10 @@ private:
doReturn(makeLoad(internString(RETURN_NAME), node)); doReturn(makeLoad(internString(RETURN_NAME), node));
break; break;
case Why::BREAK: case Why::BREAK:
doBreak(); doBreak(node);
break; break;
case Why::CONTINUE: case Why::CONTINUE:
doContinue(); doContinue(node);
break; break;
case Why::FALLTHROUGH: case Why::FALLTHROUGH:
assert(exit_block); assert(exit_block);
...@@ -1973,7 +1973,7 @@ public: ...@@ -1973,7 +1973,7 @@ public:
bool visit_break(AST_Break* node) override { bool visit_break(AST_Break* node) override {
assert(curblock); assert(curblock);
doBreak(); doBreak(node);
assert(!curblock); assert(!curblock);
return true; return true;
} }
...@@ -1981,7 +1981,7 @@ public: ...@@ -1981,7 +1981,7 @@ public:
bool visit_continue(AST_Continue* node) override { bool visit_continue(AST_Continue* node) override {
assert(curblock); assert(curblock);
doContinue(); doContinue(node);
assert(!curblock); assert(!curblock);
return true; return true;
} }
......
// Copyright (c) 2014-2015 Dropbox, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <algorithm>
#include <cmath>
#include <langinfo.h>
#include <sstream>
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "codegen/unwinding.h"
#include "core/ast.h"
#include "core/types.h"
#include "runtime/file.h"
#include "runtime/inline/boxing.h"
#include "runtime/int.h"
#include "runtime/types.h"
#include "runtime/util.h"
namespace pyston {
static BoxedClass* AST_cls;
class BoxedAST : public Box {
public:
AST* ast;
BoxedAST() {}
};
static std::unordered_map<int, BoxedClass*> type_to_cls;
Box* boxAst(AST* ast) {
assert(ast);
BoxedClass* cls = type_to_cls[ast->type];
assert(cls);
BoxedAST* rtn = new (cls) BoxedAST();
assert(rtn->cls == cls);
rtn->ast = ast;
return rtn;
}
AST* unboxAst(Box* b) {
assert(isSubclass(b->cls, AST_cls));
AST* rtn = static_cast<BoxedAST*>(b)->ast;
assert(rtn);
return rtn;
}
extern "C" int PyAST_Check(PyObject* o) noexcept {
return isSubclass(o->cls, AST_cls);
}
void setupAST() {
BoxedModule* ast_module = createModule(autoDecref(boxString("_ast")), "__builtin__");
ast_module->giveAttr("PyCF_ONLY_AST", boxInt(PyCF_ONLY_AST));
// ::create takes care of registering the class as a GC root.
#define MAKE_CLS(name, base_cls) \
BoxedClass* name##_cls \
= BoxedClass::create(type_cls, base_cls, 0, 0, sizeof(BoxedAST), false, STRINGIFY(name), NULL, NULL, false); \
ast_module->giveAttrBorrowed(STRINGIFY(name), name##_cls); \
type_to_cls[AST_TYPE::name] = name##_cls; \
name##_cls->giveAttr("__module__", boxString("_ast")); \
name##_cls->freeze()
AST_cls = BoxedClass::create(type_cls, object_cls, 0, 0, sizeof(BoxedAST), false, "AST", NULL, NULL, false);
// ::create takes care of registering the class as a GC root.
AST_cls->giveAttr("__module__", boxString("_ast"));
AST_cls->freeze();
// TODO(kmod) you can call the class constructors, such as "ast.AST()", so we need new/init
// TODO(kmod) there is more inheritance than "they all inherit from AST"
MAKE_CLS(alias, AST_cls);
MAKE_CLS(arguments, AST_cls);
MAKE_CLS(Assert, AST_cls);
MAKE_CLS(Assign, AST_cls);
MAKE_CLS(Attribute, AST_cls);
MAKE_CLS(AugAssign, AST_cls);
MAKE_CLS(BinOp, AST_cls);
MAKE_CLS(BoolOp, AST_cls);
MAKE_CLS(Call, AST_cls);
MAKE_CLS(ClassDef, AST_cls);
MAKE_CLS(Compare, AST_cls);
MAKE_CLS(comprehension, AST_cls);
MAKE_CLS(Delete, AST_cls);
MAKE_CLS(Dict, AST_cls);
MAKE_CLS(Exec, AST_cls);
MAKE_CLS(ExceptHandler, AST_cls);
MAKE_CLS(ExtSlice, AST_cls);
MAKE_CLS(Expr, AST_cls);
MAKE_CLS(For, AST_cls);
MAKE_CLS(FunctionDef, AST_cls);
MAKE_CLS(GeneratorExp, AST_cls);
MAKE_CLS(Global, AST_cls);
MAKE_CLS(If, AST_cls);
MAKE_CLS(IfExp, AST_cls);
MAKE_CLS(Import, AST_cls);
MAKE_CLS(ImportFrom, AST_cls);
MAKE_CLS(Index, AST_cls);
MAKE_CLS(keyword, AST_cls);
MAKE_CLS(Lambda, AST_cls);
MAKE_CLS(List, AST_cls);
MAKE_CLS(ListComp, AST_cls);
MAKE_CLS(Module, AST_cls);
MAKE_CLS(Num, AST_cls);
MAKE_CLS(Name, AST_cls);
MAKE_CLS(Pass, AST_cls);
MAKE_CLS(Pow, AST_cls);
MAKE_CLS(Print, AST_cls);
MAKE_CLS(Raise, AST_cls);
MAKE_CLS(Repr, AST_cls);
MAKE_CLS(Return, AST_cls);
MAKE_CLS(Slice, AST_cls);
MAKE_CLS(Str, AST_cls);
MAKE_CLS(Subscript, AST_cls);
MAKE_CLS(TryExcept, AST_cls);
MAKE_CLS(TryFinally, AST_cls);
MAKE_CLS(Tuple, AST_cls);
MAKE_CLS(UnaryOp, AST_cls);
MAKE_CLS(With, AST_cls);
MAKE_CLS(While, AST_cls);
MAKE_CLS(Yield, AST_cls);
MAKE_CLS(Store, AST_cls);
MAKE_CLS(Load, AST_cls);
MAKE_CLS(Param, AST_cls);
MAKE_CLS(Not, AST_cls);
MAKE_CLS(In, AST_cls);
MAKE_CLS(Is, AST_cls);
MAKE_CLS(IsNot, AST_cls);
MAKE_CLS(Or, AST_cls);
MAKE_CLS(And, AST_cls);
MAKE_CLS(Eq, AST_cls);
MAKE_CLS(NotEq, AST_cls);
MAKE_CLS(NotIn, AST_cls);
MAKE_CLS(GtE, AST_cls);
MAKE_CLS(Gt, AST_cls);
MAKE_CLS(Mod, AST_cls);
MAKE_CLS(Add, AST_cls);
MAKE_CLS(Continue, AST_cls);
MAKE_CLS(Lt, AST_cls);
MAKE_CLS(LtE, AST_cls);
MAKE_CLS(Break, AST_cls);
MAKE_CLS(Sub, AST_cls);
MAKE_CLS(Del, AST_cls);
MAKE_CLS(Mult, AST_cls);
MAKE_CLS(Div, AST_cls);
MAKE_CLS(USub, AST_cls);
MAKE_CLS(BitAnd, AST_cls);
MAKE_CLS(BitOr, AST_cls);
MAKE_CLS(BitXor, AST_cls);
MAKE_CLS(RShift, AST_cls);
MAKE_CLS(LShift, AST_cls);
MAKE_CLS(Invert, AST_cls);
MAKE_CLS(UAdd, AST_cls);
MAKE_CLS(FloorDiv, AST_cls);
MAKE_CLS(DictComp, AST_cls);
MAKE_CLS(Set, AST_cls);
MAKE_CLS(Ellipsis, AST_cls);
MAKE_CLS(Expression, AST_cls);
MAKE_CLS(SetComp, AST_cls);
MAKE_CLS(Suite, AST_cls);
#undef MAKE_CLS
// Uncommenting this makes `import ast` work, which may or may not be desired.
// For now it seems like making the import fail is better than having the module not work properly.
// ast_module->giveAttr("__version__", boxInt(82160));
}
}
...@@ -1222,8 +1222,8 @@ extern "C" int PyRun_InteractiveOneFlags(FILE* fp, const char* filename, PyCompi ...@@ -1222,8 +1222,8 @@ extern "C" int PyRun_InteractiveOneFlags(FILE* fp, const char* filename, PyCompi
assert(PyModule_Check(m)); assert(PyModule_Check(m));
bool failed = false; bool failed = false;
try { try {
AST_Module* pyston_module = cpythonToPystonAST(mod, filename); assert(mod->kind == Interactive_kind);
makeModuleInteractive(pyston_module); AST_Module* pyston_module = static_cast<AST_Module*>(cpythonToPystonAST(mod, filename));
compileAndRunModule(pyston_module, static_cast<BoxedModule*>(m)); compileAndRunModule(pyston_module, static_cast<BoxedModule*>(m));
} catch (ExcInfo e) { } catch (ExcInfo e) {
setCAPIException(e); setCAPIException(e);
......
...@@ -91,6 +91,7 @@ extern "C" void init_sqlite3(); ...@@ -91,6 +91,7 @@ extern "C" void init_sqlite3();
extern "C" void PyMarshal_Init(); extern "C" void PyMarshal_Init();
extern "C" void initstrop(); extern "C" void initstrop();
extern "C" void initgc(); extern "C" void initgc();
extern "C" void init_ast();
namespace pyston { namespace pyston {
...@@ -4231,7 +4232,6 @@ void setupRuntime() { ...@@ -4231,7 +4232,6 @@ void setupRuntime() {
setupImport(); setupImport();
initgc(); initgc();
setupPyston(); setupPyston();
setupAST();
PyType_Ready(&PyByteArrayIter_Type); PyType_Ready(&PyByteArrayIter_Type);
PyType_Ready(&PyCapsule_Type); PyType_Ready(&PyCapsule_Type);
...@@ -4277,6 +4277,7 @@ void setupRuntime() { ...@@ -4277,6 +4277,7 @@ void setupRuntime() {
init_sqlite3(); init_sqlite3();
PyMarshal_Init(); PyMarshal_Init();
initstrop(); initstrop();
init_ast();
assert(object_cls->tp_setattro == PyObject_GenericSetAttr); assert(object_cls->tp_setattro == PyObject_GenericSetAttr);
assert(none_cls->tp_setattro == PyObject_GenericSetAttr); assert(none_cls->tp_setattro == PyObject_GenericSetAttr);
......
from __future__ import print_function
import _ast
# test compile string:
a = compile("'hello world'", "test.py", "single", _ast.PyCF_ONLY_AST)
exec compile(a, "test.py", "single")
compile('1 + 1', 'test.py', 'eval', dont_inherit=True)
# test compile ast:
def hello():
print('hello again')
tree = compile('hello()', 'test.py', 'eval', _ast.PyCF_ONLY_AST)
exec compile(tree, '<tree>', 'eval', dont_inherit=True)
# test future flags:
exec compile('print(1, 2)', 'test.py', 'exec')
tree = compile('print(1, 2)', 'test.py', 'exec', _ast.PyCF_ONLY_AST)
exec compile(tree, '<tree>', 'exec')
# test bad syntax which should not raise in compile time:
try:
exec compile('break', '?', 'exec')
assert False
except SyntaxError:
pass
try:
exec compile('continue', '?', 'exec')
assert False
except SyntaxError:
pass
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment