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

Interactive shell implementation #2617

Merged
merged 9 commits into from
May 12, 2024

Conversation

Vipul-Cariappa
Copy link
Contributor

@Vipul-Cariappa Vipul-Cariappa commented Mar 19, 2024

Fixes #329

TODOs

  • Remember global functions and variables declared.
  • Error message with proper line number and highlighting
  • Intrinsic functions support
  • print the value of the evaluated expression
  • Import other modules
  • symengine and numpy support
  • Support complex data types like list, tuple and dict

Got the following things to work:
image

@Vipul-Cariappa
Copy link
Contributor Author

I want to know how should I proceed with the first to-do point "Remember global functions and variables declared". The problem is as follows

>>> def f():
...   print("called function f")
... 
>>> f()
semantic error: Function 'f' is not declared and not intrinsic
 --> input:1:1
  |
1 | f()
  | ^ 

Another example:

>>> x: i32 = 10
>>> print(x)
semantic error: Variable 'x' not declared
 --> input:1:1
  |
1 | print(x)
  | ^ 

The declared function f is lost by the time we evaluate (i.e. parse and execute) the function call to f. We should store its declaration (and x's declaration) somewhere (I believe at the AST level) and include it while parsing the second part/prompt.

I want to know if there are existing methods to do this. Or should I implement it myself. I want something like.
get_all_function_declerations(AST_t*) and get_all_global_declerations(AST_t*), and extend_ast_with_function_decleration(AST_t*, Function_t*) and extend_ast_with_global_decleration(AST_t*, ...)

@Shaikh-Ubaid
Copy link
Collaborator

I think the idea to solve this is to keep updating the original ASR on every new interactive query run by the user.

On every run, the functions or variables defined in the previous run need to be updated. We remove the body of functions and set their abiType to interactive. Similarly for variables, we mark them as interactive. Later we handle the interactive functions/variables appropriately in the AST->ASR, ASR passes and ASR->backend.

LFortran already supports interactive compilation. You can look into it for reference. Since the ASR passes and backends are shared with LFortran, I think we will mostly need interactive support for AST->ASR in LPython.

@Vipul-Cariappa Vipul-Cariappa force-pushed the interactive branch 3 times, most recently from 5b9a184 to a18035d Compare May 8, 2024 13:45
@Shaikh-Ubaid
Copy link
Collaborator

Shaikh-Ubaid commented May 8, 2024

Let's build this iteratively. For this PR, let's get something that works with the interactive shell, add tests for the implemented support and ensure the CI passes. After that, you can send more PRs to improve the existing support and/or add more features as planned in #2617 (comment). Ideally, small and frequent PRs that just focus on one particular support with robust tests are preferable.

For tests, you can look into how lfortran does it https://github.com/lfortran/lfortran/blob/582db3d4379eee26cfeb0721d5fda21971aaae3d/src/lfortran/tests/test_llvm.cpp#L439-L455.

Feel free to reach out if you need any help.

@Shaikh-Ubaid
Copy link
Collaborator

Please mark this as "Ready for review" when ready.

@Vipul-Cariappa
Copy link
Contributor Author

It makes more sense to have multiple smaller PR than a large one. I will finish up the work with remembering context from the previous cells, i.e. remembering global functions and variables.
We can add in tests and merge this PR. The other points can be their own separate PRs.

@Vipul-Cariappa Vipul-Cariappa marked this pull request as ready for review May 10, 2024 03:34
Comment on lines 200 to 203
Result<std::unique_ptr<LCompilers::LLVMModule>> res
= asr_to_llvm(asr, diagnostics,
e->get_context(), al, lpm, compiler_options,
run_fn, infile);
"", infile); // ??? What about run function
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 argument "" is the run_fn. The run_fn is not used here. In LFortran all global statements are compiled into 1 function, but in LPython it is compiled into two different functions one for global initialization and the other to execute global statements.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I see. Do you need the above change of run_fn to "" for the interactivity to run/work? Or is the above just a refactoring change?

If it is a refactoring change (which it seems to be), I suggest we do it in a separate PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now that we have merged global_init and global_stmts, we can assign run_fn to global_stmts and use it.

Then that change would be part of this PR. You can have a look at it in successive commits.

Comment on lines 5038 to 5009
static size_t interactive_execution_count = 0; // ???: should this be a class member variable
// interactive_execution_count should be the same as eval_count in PythonCompiler
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Any suggestions on this?
Should we create run_fn1 and run_fn2 and share it through the PassOptions?
But even that may not be straightforward.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Another option would be to combine the init and stmts functions. Look at #2573

Copy link
Collaborator

Choose a reason for hiding this comment

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

I saw the linked issue. If combining init and stmt function solves your issue, then you can try it. Submit it as a separate PR and we can rebase this after that is merged.

@Vipul-Cariappa
Copy link
Contributor Author

I guess we can merge the work done till this point. The redefinition of symbols is not yet supported. It looks more complicated than initially thought. We can add support for the redefinition of symbols in a later PR.

Not sure why the tests are failing.

I have left 2 comments please have a look at it.

@Shaikh-Ubaid
Copy link
Collaborator

You need to update the reference tests. You can do ./run_tests.py -u.

@Shaikh-Ubaid
Copy link
Collaborator

At a high level, it looks good. I will review in detail in evening today.

Copy link
Collaborator

@Shaikh-Ubaid Shaikh-Ubaid left a comment

Choose a reason for hiding this comment

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

I shared a couple comments.

Mainly:

Apart from that, it looks good. Great start! Amazing work!

src/bin/lpython.cpp Outdated Show resolved Hide resolved
Comment on lines 200 to 203
Result<std::unique_ptr<LCompilers::LLVMModule>> res
= asr_to_llvm(asr, diagnostics,
e->get_context(), al, lpm, compiler_options,
run_fn, infile);
"", infile); // ??? What about run function
Copy link
Collaborator

Choose a reason for hiding this comment

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

I see. Do you need the above change of run_fn to "" for the interactivity to run/work? Or is the above just a refactoring change?

If it is a refactoring change (which it seems to be), I suggest we do it in a separate PR.

src/lpython/parser/parser.cpp Outdated Show resolved Hide resolved
@@ -8,7 +8,7 @@ namespace LCompilers::LPython {

Result<ASR::TranslationUnit_t*> python_ast_to_asr(Allocator &al, LocationManager &lm, SymbolTable* symtab,
LPython::AST::ast_t &ast, diag::Diagnostics &diagnostics, CompilerOptions &compiler_options,
bool main_module, std::string module_name, std::string file_path, bool allow_implicit_casting=false);
bool main_module, std::string module_name, std::string file_path, bool allow_implicit_casting=false, bool is_interactive=false);
Copy link
Collaborator

Choose a reason for hiding this comment

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

You can also pass the eval_count here and have it as a class member. Then for body visitor inside visit_Module (assuming we are reusing this instead of defining visit_Interactive), you can use the eval_count for naming global_init and global_stmts if interactive = true. (For example simply append the eval_count to the end if interactive is true).

src/lpython/semantics/python_ast_to_asr.cpp Outdated Show resolved Hide resolved
src/lpython/python_evaluator.cpp Outdated Show resolved Hide resolved
src/lpython/python_evaluator.cpp Outdated Show resolved Hide resolved
src/bin/lpython.cpp Outdated Show resolved Hide resolved
@Shaikh-Ubaid Shaikh-Ubaid marked this pull request as draft May 11, 2024 05:37
@Shaikh-Ubaid
Copy link
Collaborator

Please mark as "Ready for review" when ready.

Copy link
Collaborator

@Shaikh-Ubaid Shaikh-Ubaid left a comment

Choose a reason for hiding this comment

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

Good! Few more minor comments. I think it will be good to merge after that. We can add tests in a separate PR.

Comment on lines 1 to 2
#include "libasr/asr_scopes.h"
#include "lpython/python_ast.h"
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 use <> instead of "" here and place this along with other similar includes below.

@@ -1778,6 +1935,7 @@ int main(int argc, char *argv[])
compiler_options.use_colors = !arg_no_color;
compiler_options.indent = !arg_no_indent;


Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change

#include <libasr/codegen/asr_to_cpp.h>
#include <libasr/exception.h>
#include <libasr/asr.h>
#include <lpython/pickle.h>
#include <lpython/parser/parser.h>
#include <string>
Copy link
Collaborator

@Shaikh-Ubaid Shaikh-Ubaid May 11, 2024

Choose a reason for hiding this comment

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

I think this should be included with other system headers at the top. I am unsure if system headers should be listed first and then the custom headers or the other way round. But let's atleast place them together.

Comment on lines -50 to -52
eval_count++;
run_fn = "__lfortran_evaluate_" + std::to_string(eval_count);

Copy link
Collaborator

Choose a reason for hiding this comment

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

We later plan to move the interactivity support into the LibASR so that it is shared between LFortran, LPython and LC. So, it is better that we have/use as common/generic code as possible which easily gets shared across all LCompilers.

Assuming it does not affect the newly added interactivity, I think for now it is better to have it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can we keep this as part of the next PR? It may require changes in more than one place.

I was working towards "print the value of the evaluated expression" (#2617 (comment)) goal, and I think there might be some obstacles to doing this. But is doable.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we keep this as part of the next PR?

We can do that.

@@ -59,10 +71,10 @@ class PythonCompiler
Allocator al;
#ifdef HAVE_LFORTRAN_LLVM
std::unique_ptr<LLVMEvaluator> e;
int eval_count;
size_t eval_count;
Copy link
Collaborator

@Shaikh-Ubaid Shaikh-Ubaid May 11, 2024

Choose a reason for hiding this comment

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

This is passed as argument to python_ast_to_asr(). The call to python_ast_to_asr() is also used by other backends. So, this should be declared outside of #ifdef HAVE_LFORTRAN_LLVM.

PS: The above will fix the CI failure.

@Vipul-Cariappa Vipul-Cariappa marked this pull request as ready for review May 12, 2024 04:13
@Vipul-Cariappa
Copy link
Contributor Author

I have made the suggested changes. Please have a look. I have also left a comment on one of your suggestions.

Copy link
Collaborator

@Shaikh-Ubaid Shaikh-Ubaid left a comment

Choose a reason for hiding this comment

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

It looks good! Thanks for this! Great work!

Comment on lines -50 to -52
eval_count++;
run_fn = "__lfortran_evaluate_" + std::to_string(eval_count);

Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we keep this as part of the next PR?

We can do that.

@Shaikh-Ubaid Shaikh-Ubaid enabled auto-merge (squash) May 12, 2024 05:31
@Shaikh-Ubaid
Copy link
Collaborator

Shaikh-Ubaid commented May 12, 2024

Let's add some tests for this in the next PR!

@Shaikh-Ubaid Shaikh-Ubaid merged commit 476d9bb into lcompilers:main May 12, 2024
14 checks passed
@Vipul-Cariappa Vipul-Cariappa deleted the interactive branch May 12, 2024 13:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Interactivity
2 participants