Hi, This is fun experiment: Every RBValueNode subclass evaluates to something. So why not implement a #evaluate method? This is especially fun to do for a specific context, as this means that you could evaluate every RBVariableNode to see the value. So lets implement a method in RBValueNode: evaluateWithContext: aContext | methodNode | If we want to compile a method for it, we need to wrap it into a return and a method. Like when evaluating in the debugger, we create a #DoItIn: method that will get the context as a parameter to read the values from (temps from the context, ivars from context receiver). This works like this: methodNode := RBMethodNode selector: #DoItIn: arguments: { RBVariableNode named: 'ThisContext' } body: (RBReturnNode value: self) asSequenceNode. to read temps from the context parameter, we use the rewrite method that is used by doits already: methodNode rewriteTempsForContext: aContext. and now we can execute: ^methodNode generate valueWithReceiver: aContext receiver arguments: {aContext}. For testing, I put a #haltOnce into Rectangle>>#area and then use the inspector on the context to evaluate a AST node that is a temporary variable. The screenshot shows executing self method ast assignmentNodes first variable evaluateWithContext: self and it returns correctly 104: Of course there are simpler ways to read variables by name from a context (e.g. we added #lookupSymbol: ), but #evaluateWithContext: can be called on other Nodes, too. E.g. just a #evaluate could be nice for creating Blocks programmatically (instead of calling the compiler on a Pretty-Print result like #createBlockFor: in the ParseTreeSearcher). A version that does not take context and receiver into account is this: evaluate | methodNode | methodNode := RBMethodNode selector: #DoIt body: (RBReturnNode value: self) asSequenceNode. ^methodNode generate valueWithReceiver: nil arguments: #(). and then you can do things like blockNode: = RBBlockNode body: (RBLiteralNode value: 5) asSequenceNode blockNode evaluate to create blocks. Marcus
Starting from here you can build a full AST interpreter that mutates a context instance that you create by interpreting MessageNode... On Tue, Apr 17, 2018 at 5:45 PM, Marcus Denker <marcus.denker@inria.fr> wrote:
Hi,
This is fun experiment:
Every RBValueNode subclass evaluates to something. So why not implement a #evaluate method?
This is especially fun to do for a specific context, as this means that you could evaluate every RBVariableNode to see the value.
So lets implement a method in RBValueNode:
evaluateWithContext: aContext
| methodNode |
If we want to compile a method for it, we need to wrap it into a return and a method. Like when evaluating in the debugger, we create a #DoItIn: method that will get the context as a parameter to read the values from (temps from the context, ivars from context receiver).
This works like this:
methodNode := RBMethodNode selector: #DoItIn: arguments: { RBVariableNode named: 'ThisContext' } body: (RBReturnNode value: self) asSequenceNode.
to read temps from the context parameter, we use the rewrite method that is used by doits already: methodNode rewriteTempsForContext: aContext.
and now we can execute:
^methodNode generate valueWithReceiver: aContext receiver arguments: {aContext}.
For testing, I put a #haltOnce into Rectangle>>#area and then use the inspector on the context to evaluate a AST node that is a temporary variable. The screenshot shows executing
self method ast assignmentNodes first variable evaluateWithContext: self
and it returns correctly 104:
Of course there are simpler ways to read variables by name from a context (e.g. we added #lookupSymbol: ), but #evaluateWithContext: can be called on other Nodes, too. E.g. just a #evaluate could be nice for creating Blocks programmatically (instead of calling the compiler on a Pretty-Print result like #createBlockFor: in the ParseTreeSearcher).
A version that does not take context and receiver into account is this:
evaluate | methodNode | methodNode := RBMethodNode selector: #DoIt body: (RBReturnNode value: self) asSequenceNode. ^methodNode generate valueWithReceiver: nil arguments: #().
and then you can do things like
blockNode: = RBBlockNode body: (RBLiteralNode value: 5) asSequenceNode
blockNode evaluate
to create blocks.
Marcus
-- Clément Béra https://clementbera.github.io/ https://clementbera.wordpress.com/
So this means that we could also have a simple hover showing the variable values when debugging. Now clement what is the difference with a plain AST interpreter? On Tue, Apr 17, 2018 at 6:57 PM, Clément Bera <bera.clement@gmail.com> wrote:
Starting from here you can build a full AST interpreter that mutates a context instance that you create by interpreting MessageNode...
On Tue, Apr 17, 2018 at 5:45 PM, Marcus Denker <marcus.denker@inria.fr> wrote:
Hi,
This is fun experiment:
Every RBValueNode subclass evaluates to something. So why not implement a #evaluate method?
This is especially fun to do for a specific context, as this means that you could evaluate every RBVariableNode to see the value.
So lets implement a method in RBValueNode:
evaluateWithContext: aContext
| methodNode |
If we want to compile a method for it, we need to wrap it into a return and a method. Like when evaluating in the debugger, we create a #DoItIn: method that will get the context as a parameter to read the values from (temps from the context, ivars from context receiver).
This works like this:
methodNode := RBMethodNode selector: #DoItIn: arguments: { RBVariableNode named: 'ThisContext' } body: (RBReturnNode value: self) asSequenceNode.
to read temps from the context parameter, we use the rewrite method that is used by doits already: methodNode rewriteTempsForContext: aContext.
and now we can execute:
^methodNode generate valueWithReceiver: aContext receiver arguments: {aContext}.
For testing, I put a #haltOnce into Rectangle>>#area and then use the inspector on the context to evaluate a AST node that is a temporary variable. The screenshot shows executing
self method ast assignmentNodes first variable evaluateWithContext: self
and it returns correctly 104:
Of course there are simpler ways to read variables by name from a context (e.g. we added #lookupSymbol: ), but #evaluateWithContext: can be called on other Nodes, too. E.g. just a #evaluate could be nice for creating Blocks programmatically (instead of calling the compiler on a Pretty-Print result like #createBlockFor: in the ParseTreeSearcher).
A version that does not take context and receiver into account is this:
evaluate | methodNode | methodNode := RBMethodNode selector: #DoIt body: (RBReturnNode value: self) asSequenceNode. ^methodNode generate valueWithReceiver: nil arguments: #().
and then you can do things like
blockNode: = RBBlockNode body: (RBLiteralNode value: 5) asSequenceNode
blockNode evaluate
to create blocks.
Marcus
-- Clément Béra https://clementbera.github.io/ https://clementbera.wordpress.com/
On 18 Apr 2018, at 07:41, Stephane Ducasse <stepharo.self@gmail.com> wrote:
So this means that we could also have a simple hover showing the variable values when debugging.
It could be used for that, but then we did have already lookupSymbol: on context (we added that so we can easily use any valid variable in the condition for conditional breakpoints).
Now clement what is the difference with a plain AST interpreter?
For larger ASTs it should be faster (especially with loops), for just reading a var the compilation overhead should be quite high. Did not measure it, though. The nice thing that is is not a lot of code *and* it is the same code we use to evaluate DoIts already, so with some refactoring is would be (from a LOC perspective) completely free and even simplify the Doit logic. Marcus
On 18 Apr 2018, at 08:11, Marcus Denker <marcus.denker@inria.fr> wrote:
On 18 Apr 2018, at 07:41, Stephane Ducasse <stepharo.self@gmail.com> wrote:
So this means that we could also have a simple hover showing the variable values when debugging.
It could be used for that, but then we did have already lookupSymbol: on context (we added that so we can easily use any valid variable in the condition for conditional breakpoints).
Hmm⦠now that I think about it⦠I guess evaluateWithContext: could even be used to simplify the code that creates the condition block for conditional breakpoints. This was not even the goal⦠to be checked. Marcus
On 18 Apr 2018, at 08:34, Marcus Denker <marcus.denker@inria.fr> wrote:
On 18 Apr 2018, at 08:11, Marcus Denker <marcus.denker@inria.fr> wrote:
On 18 Apr 2018, at 07:41, Stephane Ducasse <stepharo.self@gmail.com> wrote:
So this means that we could also have a simple hover showing the variable values when debugging.
It could be used for that, but then we did have already lookupSymbol: on context (we added that so we can easily use any valid variable in the condition for conditional breakpoints).
Hmm⦠now that I think about it⦠I guess evaluateWithContext: could even be used to simplify the code that creates the condition block for conditional breakpoints.
This was not even the goal⦠to be checked.
Marcus
You have to do enough fun stuff, that is very important.
2018-04-18 7:41 GMT+02:00 Stephane Ducasse <stepharo.self@gmail.com>:
Now clement what is the difference with a plain AST interpreter?
Together with #evaluate we can also have #debug message. With interpreter approach it would be tricky to implement it with standard debugger.
On Tue, Apr 17, 2018 at 6:57 PM, Clément Bera <bera.clement@gmail.com> wrote:
Starting from here you can build a full AST interpreter that mutates a context instance that you create by interpreting MessageNode...
On Tue, Apr 17, 2018 at 5:45 PM, Marcus Denker <marcus.denker@inria.fr> wrote:
Hi,
This is fun experiment:
Every RBValueNode subclass evaluates to something. So why not implement a #evaluate method?
This is especially fun to do for a specific context, as this means that you could evaluate every RBVariableNode to see the value.
So lets implement a method in RBValueNode:
evaluateWithContext: aContext
| methodNode |
If we want to compile a method for it, we need to wrap it into a return and a method. Like when evaluating in the debugger, we create a #DoItIn: method that will get the context as a parameter to read the values from (temps from the context, ivars from context receiver).
This works like this:
methodNode := RBMethodNode selector: #DoItIn: arguments: { RBVariableNode named: 'ThisContext' } body: (RBReturnNode value: self) asSequenceNode.
to read temps from the context parameter, we use the rewrite method that is used by doits already: methodNode rewriteTempsForContext: aContext.
and now we can execute:
^methodNode generate valueWithReceiver: aContext receiver arguments: {aContext}.
For testing, I put a #haltOnce into Rectangle>>#area and then use the inspector on the context to evaluate a AST node that is a temporary variable. The screenshot shows executing
self method ast assignmentNodes first variable evaluateWithContext: self
and it returns correctly 104:
Of course there are simpler ways to read variables by name from a context (e.g. we added #lookupSymbol: ), but #evaluateWithContext: can be called on other Nodes, too. E.g. just a #evaluate could be nice for creating Blocks programmatically (instead of calling the compiler on a Pretty-Print result like #createBlockFor: in the ParseTreeSearcher).
A version that does not take context and receiver into account is this:
evaluate | methodNode | methodNode := RBMethodNode selector: #DoIt body: (RBReturnNode value: self) asSequenceNode. ^methodNode generate valueWithReceiver: nil arguments: #().
and then you can do things like
blockNode: = RBBlockNode body: (RBLiteralNode value: 5) asSequenceNode
blockNode evaluate
to create blocks.
Marcus
-- Clément Béra https://clementbera.github.io/ https://clementbera.wordpress.com/
On 17 Apr 2018, at 17:45, Marcus Denker <marcus.denker@inria.fr> wrote:
Hi,
This is fun experiment:
Every RBValueNode subclass evaluates to something. So why not implement a #evaluate method?
I have committed the code (with tests) here: https://github.com/pharo-project/pharo/pull/1214 <https://github.com/pharo-project/pharo/pull/1214> Marcus
participants (5)
-
Clément Bera -
Denis Kudriashov -
Marcus Denker -
Stephane Ducasse -
Sven Van Caekenberghe