Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion src/lpython/python_evaluator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,48 @@ Result<PythonCompiler::EvalResult> PythonCompiler::evaluate(
}

bool call_run_fn = false;
std::string return_type;
if (m->get_return_type(run_fn) != "none") {
call_run_fn = true;
return_type = m->get_return_type(run_fn);
}

e->add_module(std::move(m));
if (call_run_fn) {
e->voidfn(run_fn);
if (return_type == "integer4") {
int32_t r = e->int32fn(run_fn);
result.type = EvalResult::integer4;
result.i32 = r;
} else if (return_type == "integer8") {
int64_t r = e->int64fn(run_fn);
result.type = EvalResult::integer8;
result.i64 = r;
} else if (return_type == "real4") {
float r = e->floatfn(run_fn);
result.type = EvalResult::real4;
result.f32 = r;
} else if (return_type == "real8") {
double r = e->doublefn(run_fn);
result.type = EvalResult::real8;
result.f64 = r;
} else if (return_type == "complex4") {
std::complex<float> r = e->complex4fn(run_fn);
result.type = EvalResult::complex4;
result.c32.re = r.real();
result.c32.im = r.imag();
} else if (return_type == "complex8") {
std::complex<double> r = e->complex8fn(run_fn);
result.type = EvalResult::complex8;
result.c64.re = r.real();
result.c64.im = r.imag();
} else if (return_type == "void") {
e->voidfn(run_fn);
result.type = EvalResult::statement;
} else if (return_type == "none") {
result.type = EvalResult::none;
} else {
throw LCompilersException("FortranEvaluator::evaluate(): Return type not supported");
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we previously add all the above code somewhere else? (It feels like we added it somewhere before).

If this is being duplicated then I think we should create a function out of it and call that function here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had initially implemented this in #2698, but we did not merge. That PR lacked tests. We also merged other changes between that PR and this. I was thinking of closing that PR without merging it once we merge this PR.

}

if (call_run_fn) {
Expand Down
13 changes: 9 additions & 4 deletions src/lpython/semantics/python_ast_to_asr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6693,10 +6693,15 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
}
this->visit_expr(*x.m_value);

// If tmp is a statement and not an expression
// never cast into expression using ASRUtils::EXPR
// Just ignore and exit the function naturally.
if( tmp && !ASR::is_a<ASR::stmt_t>(*tmp) ) {
if (eval_count > 0) {
// In Interactive mode
if ((tmp) && (!ASR::is_a<ASR::expr_t>(*tmp))) {
tmp = nullptr;
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am a bit confused here. We make tmp as nullptr everytime it is not an expression. But, why do we do so? I think this means, if tmp is a statement, then we are skipping it. Any specific reason to skip statements @Vipul-Cariappa ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If tmp is an expression then we return it. So that we can print it. The pass_wrap_global_stmts function uses this tmp expression and returns the evaluated result of the expression for us to print it later using PythonCompiler::evaluate and interactive_python_repl.
If tmp is a statement, for example, an assignment x = 5 then we would not want to print anything, so we set tmp to nullptr.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

x = 5 then we would not want to print anything, so we set tmp to nullptr

Although we do not want to print anything, but we do want x to be updated to 5. I am unsure, but if we skip the whole x = 5 statement, then would the value of x be updated to 5?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pass_wrap_global_stmts function uses this tmp expression and returns the evaluated result of the expression for us

Does it actually return an evaluated result? Or does it simply convert expressions into assignment statements?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. The statement will be executed and x will be updated to 5.

❯ lp
>>> x: i32
>>> x
0
>>> x = 5
>>> x
5

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it actually return an evaluated result? Or does it simply convert expressions into assignment statements?

It returns the evaluated result. You can run the interactive in verbose mode with -v flag to see the AST, ASR, and the LLVM IR generated. Doing so, you can see that it returns the result.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the current approach, I doubt that we are/would-be skipping some stmts like empty() and c_p_pointer()

// This happens for c_p_pointer() and
// empty() (if target is of type allocatable)

See my comment below https://github.com/lcompilers/lpython/pull/2716/files#r1614616838 which I think would avoid this.

} else if (tmp && !ASR::is_a<ASR::stmt_t>(*tmp)) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you check if doing the following works?

Suggested change
if (eval_count > 0) {
// In Interactive mode
if ((tmp) && (!ASR::is_a<ASR::expr_t>(*tmp))) {
tmp = nullptr;
}
} else if (tmp && !ASR::is_a<ASR::stmt_t>(*tmp)) {
if (eval_count == 0 && tmp && !ASR::is_a<ASR::stmt_t>(*tmp)) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes work. I have committed it.

// If tmp is a statement and not an expression
// never cast into expression using ASRUtils::EXPR
// Just ignore and exit the function naturally.
LCOMPILERS_ASSERT(ASR::is_a<ASR::expr_t>(*tmp));
tmp = nullptr;
}
Expand Down
165 changes: 164 additions & 1 deletion src/lpython/tests/test_llvm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -619,5 +619,168 @@ TEST_CASE("PythonCompiler 1") {
LCompilers::Result<PythonCompiler::EvalResult>
r = e.evaluate2("1");
CHECK(r.ok);
CHECK(r.result.type == PythonCompiler::EvalResult::none); // TODO: change to integer4 and check the value once printing top level expressions is implemented
CHECK(r.result.type == PythonCompiler::EvalResult::integer4);
CHECK(r.result.i32 == 1);
}

TEST_CASE("PythonCompiler i32 expressions") {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add this as a separate test and not change the existing test.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. I can do that.

CompilerOptions cu;
cu.po.disable_main = true;
cu.emit_debug_line_column = false;
cu.generate_object_code = false;
cu.interactive = true;
cu.po.runtime_library_dir = LCompilers::LPython::get_runtime_library_dir();
PythonCompiler e(cu);
LCompilers::Result<PythonCompiler::EvalResult>

r = e.evaluate2("1");
CHECK(r.ok);
CHECK(r.result.type == PythonCompiler::EvalResult::integer4);
CHECK(r.result.i32 == 1);

r = e.evaluate2("1 + 2");
CHECK(r.ok);
CHECK(r.result.type == PythonCompiler::EvalResult::integer4);
CHECK(r.result.i32 == 3);

r = e.evaluate2("1 - 2");
CHECK(r.ok);
CHECK(r.result.type == PythonCompiler::EvalResult::integer4);
CHECK(r.result.i32 == -1);

r = e.evaluate2("1 * 2");
CHECK(r.ok);
CHECK(r.result.type == PythonCompiler::EvalResult::integer4);
CHECK(r.result.i32 == 2);

r = e.evaluate2("3 ** 3");
CHECK(r.ok);
CHECK(r.result.type == PythonCompiler::EvalResult::integer4);
CHECK(r.result.i32 == 27);

r = e.evaluate2("4 // 2");
CHECK(r.ok);
CHECK(r.result.type == PythonCompiler::EvalResult::integer4);
CHECK(r.result.i32 == 2);

r = e.evaluate2("4 / 2");
CHECK(r.ok);
CHECK(r.result.type == PythonCompiler::EvalResult::real8);
CHECK(r.result.f64 == 2);
}

TEST_CASE("PythonCompiler i32 declaration") {
CompilerOptions cu;
cu.po.disable_main = true;
cu.emit_debug_line_column = false;
cu.generate_object_code = false;
cu.interactive = true;
cu.po.runtime_library_dir = LCompilers::LPython::get_runtime_library_dir();
PythonCompiler e(cu);
LCompilers::Result<PythonCompiler::EvalResult>

r = e.evaluate2("i: i32");
CHECK(r.ok);
CHECK(r.result.type == PythonCompiler::EvalResult::none);
r = e.evaluate2("i = 5");
CHECK(r.ok);
CHECK(r.result.type == PythonCompiler::EvalResult::statement);
r = e.evaluate2("i");
CHECK(r.ok);
CHECK(r.result.type == PythonCompiler::EvalResult::integer4);
CHECK(r.result.i32 == 5);
Comment on lines +682 to +691
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test here makes sure that the execution of statements is not skipped.


r = e.evaluate2("j: i32 = 9");
CHECK(r.ok);
CHECK(r.result.type == PythonCompiler::EvalResult::none);
r = e.evaluate2("j");
CHECK(r.ok);
CHECK(r.result.type == PythonCompiler::EvalResult::integer4);
CHECK(r.result.i32 == 9);

r = e.evaluate2("i + j");
CHECK(r.ok);
CHECK(r.result.type == PythonCompiler::EvalResult::integer4);
CHECK(r.result.i32 == 14);
}

TEST_CASE("PythonCompiler i64 expressions") {
CompilerOptions cu;
cu.po.disable_main = true;
cu.emit_debug_line_column = false;
cu.generate_object_code = false;
cu.interactive = true;
cu.po.runtime_library_dir = LCompilers::LPython::get_runtime_library_dir();
PythonCompiler e(cu);
LCompilers::Result<PythonCompiler::EvalResult>

r = e.evaluate2("i64(1)");
CHECK(r.ok);
CHECK(r.result.type == PythonCompiler::EvalResult::integer8);
CHECK(r.result.i64 == 1);

r = e.evaluate2("i64(1) + i64(2)");
CHECK(r.ok);
CHECK(r.result.type == PythonCompiler::EvalResult::integer8);
CHECK(r.result.i64 == 3);

r = e.evaluate2("i64(1) - i64(2)");
CHECK(r.ok);
CHECK(r.result.type == PythonCompiler::EvalResult::integer8);
CHECK(r.result.i64 == -1);

r = e.evaluate2("i64(1) * i64(2)");
CHECK(r.ok);
CHECK(r.result.type == PythonCompiler::EvalResult::integer8);
CHECK(r.result.i64 == 2);

r = e.evaluate2("i64(3) ** i64(3)");
CHECK(r.ok);
CHECK(r.result.type == PythonCompiler::EvalResult::integer8);
CHECK(r.result.i64 == 27);

r = e.evaluate2("i64(4) // i64(2)");
CHECK(r.ok);
CHECK(r.result.type == PythonCompiler::EvalResult::integer8);
CHECK(r.result.i64 == 2);

r = e.evaluate2("i64(4) / i64(2)");
CHECK(r.ok);
CHECK(r.result.type == PythonCompiler::EvalResult::real8);
CHECK(r.result.f64 == 2);
}

TEST_CASE("PythonCompiler i64 declaration") {
CompilerOptions cu;
cu.po.disable_main = true;
cu.emit_debug_line_column = false;
cu.generate_object_code = false;
cu.interactive = true;
cu.po.runtime_library_dir = LCompilers::LPython::get_runtime_library_dir();
PythonCompiler e(cu);
LCompilers::Result<PythonCompiler::EvalResult>

r = e.evaluate2("i: i64");
CHECK(r.ok);
CHECK(r.result.type == PythonCompiler::EvalResult::none);
r = e.evaluate2("i = i64(5)");
CHECK(r.ok);
CHECK(r.result.type == PythonCompiler::EvalResult::statement);
r = e.evaluate2("i");
CHECK(r.ok);
CHECK(r.result.type == PythonCompiler::EvalResult::integer8);
CHECK(r.result.i64 == 5);

r = e.evaluate2("j: i64 = i64(9)");
CHECK(r.ok);
CHECK(r.result.type == PythonCompiler::EvalResult::none);
r = e.evaluate2("j");
CHECK(r.ok);
CHECK(r.result.type == PythonCompiler::EvalResult::integer8);
CHECK(r.result.i64 == 9);

r = e.evaluate2("i + j");
CHECK(r.ok);
CHECK(r.result.type == PythonCompiler::EvalResult::integer8);
CHECK(r.result.i64 == 14);
}