Enso supports evaluation with arguments#14739
Enso supports evaluation with arguments#14739JaroslavTulach wants to merge 18 commits intodevelopfrom
Conversation
|
|
||
| var imports = | ||
| originalSrc.lines().takeWhile(isImportStatement).collect(Collectors.joining("\n")); | ||
|
|
There was a problem hiding this comment.
- This kind of string splitting and concatenation is far from perfect
- from a parser perspective this is a ridiculous attempt
- but it gets the job done for now
- What's needed?
- Enso code snippets without imports are not very useful except REPL
- hence we need to be able to
import ...andfrom ... import ...
- hence we need to be able to
- Coding without data structures is hard as well
- hence we may need an ability to specify new
typein the snippet
- hence we may need an ability to specify new
- Enso code snippets without imports are not very useful except REPL
- It can all be done with intrinsic string manipulation ...
- but maybe there is a way to do this in
TreeToIr, what do you think @kazcw?
There was a problem hiding this comment.
A question first: What do the source locations look like?
- Foreign-function based: Source locations would be within the "file" constructed from the foreign function's string (I think this is what the current implementation does)
- Host-module based: Source locations reflected the source code in the module. This might be more helpful for debugging. It also makes the IR more "compatible" for reuse, see below.
So, there are different ways to approach making things from the "host" module visible in the "foreign" module:
- string operations before parsing the foreign module (as in this current implementation)
- IR operations after parsing the foreign module. This could be very simple and efficient. The main issue is source references. The IR we would want to copy has host-module based locations; if the new source has foreign-function based locations, we can't just mix them (Unless IR knows its origin file somehow? But source references are just line:column). If we assign host-module based locations to everything, then combining the newly parsed "module" with parts of the host module is straightforward.
- semantic operations: I could imagine the imports and type definitions being "executed" just once, to populate some sort of environment, within which functions from the host module, including foreign functions, could be run. But I don't know what would be involved in doing it that way, this is in later compiler/runtime stages than I am familiar with at all.
There was a problem hiding this comment.
What do the source locations look like?
- while working on this PR I discovered some problems with source locations
- evaluation of
Debug.evalwas using incorrect source file- it used the source that contained
Debug.eval - rather the source of the code snippet
- sometimes (when the snippet was longer than original file) that might even lead to exceptions
- it used the source that contained
- I improved the situation in c43d4e7
- I should probably separate that to its own PR with a test...
- done in Avoid invalid source section bounds error in
Debug.eval#14784
Foreign-function based: Source locations would be within the "file" constructed from the foreign
- Yes, that's the approach for
foreignfunctions - Done in Debug JavaScript/Python in Enso Source #9440
- the foreign source is constructed to match the lines of the original code
- so the foreign function appears to have code locations in the original file
There was a problem hiding this comment.
- IR operations after parsing the foreign module. This could be very simple and efficient. The main issue is source references. .... If we assign host-module based locations to everything, then combining the newly parsed "module" with parts of the host module is straightforward.
- I think the biggest problem of this PR is that there are "two sources"
- one defining lambda which does imports and then invokes
Debug.eval - the other one is expression body send as an argument to
Debug.eval
Can we merge the two sources together into one? Isn't the expression body parsed in some special way (expression only)? I think it is:
- how can we keep the current semantics of
parseBlock... - but also have
importandtypedefinitions in the same source? - do we need some changes in the Rust parser code, @kazcw?
There was a problem hiding this comment.
- another thought: Do we want to change the syntax to allow this kind of expression body in
.ensofile? - because if one follows the Insight in Enso tutorial one gets a file like this:
- our existing VSCode/NetBeans support marks that file as syntactically incorrect!
…r 'unknown type_of for [n, acc]'))
Insight in Enso ManualBuilds on top of Insight & Enso Manual that required use of JavaScript. This manual is fully relying on Enso language. Create Insight ScriptSimple script to start with is for example from Standard.Base import all
IO.println "Initializing Insight: "+(Meta.type_of insight).to_text
calling ctx frame =
IO.println "Calling "+ctx.name
when = Dictionary.empty
. insert "roots" True
. insert "rootNameFilter" ".*main.*"
insight.on "enter" calling whenThe
Usage in CLIExecute either a script: sbt:enso> runEngineDistribution --vm.D=enso.dev.insight=methods.enso --run test/Base_Tests Nic
Initializing Insight: (Error: (Compile_Error.Error 'unknown type_of for com.oracle.truffle.tools.agentscript.impl.AgentObject@710afd47'))
Calling enso_dev.Base_Tests.Main::enso_dev.Base_Tests.Main::main
Calling Main.main.suite
Calling Suite_Config.find_caller_script.find_main
Calling Suite_Config.find_caller_script.find_main
Calling Suite_Config.find_caller_script.find_main
Calling Suite_Config.find_caller_script.find_main
0 tests succeeded.
0 tests failed.
0 tests skipped.
0 groups skipped. |
| var originalSrc = src.getCharacters().toString(); | ||
|
|
||
| Predicate<String> isImportStatement = | ||
| (line) -> line.startsWith("import ") || line.startsWith("from ") || line.trim().isEmpty(); |
There was a problem hiding this comment.
Why an empty line is considered an import statement deserves a comment
There was a problem hiding this comment.
Also, probably a good idea to always do line.trim() in case of misaligned/malformed code
There was a problem hiding this comment.
- this kind of "source manipulation" is the most fragile part of this PR
- I put the check for empty line there because:
- it is common to separate imports into group in our code, for example:
enso/test/Base_Tests/src/Runtime/Ref_Spec.enso
Lines 1 to 6 in 0008bcf
always do
line.trim()
- space before
from Standard.Base import allis a syntax error - this all shows that doing IR manipulations (as discussed with Keziah) would be the preferred option
| var lambdaArgs = argNames.stream().collect(Collectors.joining("-> ")); | ||
| var lambdaCode = | ||
| """ | ||
| import Standard.Base |
There was a problem hiding this comment.
Assumption that Standard.Base is always available deserves a comment. Can we make eval a builtin?
There was a problem hiding this comment.
Debug.evalis a builtin - but it still needs to be imported- as written here Enso supports evaluation with arguments #14739 (comment) this approach with "two sources" may be the root of the problem
- however it is the only one that is known to solve the issue of "evaluation with arguments" right now
- extracted from #14739 - source locations for reports of errors in `Debug.eval` scripts were wrong
Pull Request Description
foreign ensofunctions - not very useful itself but good for testing and explaining the conceptsimport- done in df4108aenso.dev.insightproperly to turn Insight on #11385Important Notes
Checklist
Please ensure that the following checklist has been satisfied before submitting the PR:
Scala,
Java,