Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Printing top-level Expressions #2716

Merged
merged 5 commits into from
May 26, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
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
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
14 changes: 10 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,16 @@ 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))) {
LCOMPILERS_ASSERT(ASR::is_a<ASR::expr_t>(*tmp));
tmp = nullptr;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I did not quite get the logic here. It says if tmp exists and it is not an expression, then right inside the if, we have assert that tmp is an expression. tmp is not an expresssion in if and tmp is an expression in the assert are contradicting, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. It does not make sense. I should remove that assertion.

}
} else if (tmp && !ASR::is_a<ASR::stmt_t>(*tmp)) {
// 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
152 changes: 150 additions & 2 deletions src/lpython/tests/test_llvm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,7 @@ define float @f()
CHECK(std::abs(r - 8) < 1e-6);
}

TEST_CASE("PythonCompiler 1") {
TEST_CASE("PythonCompiler i32 expressions") {
Copy link
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
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;
Expand All @@ -617,7 +617,155 @@ TEST_CASE("PythonCompiler 1") {
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::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);

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
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);
}
Loading