Fun with AST evaluation

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

Fun with AST evaluation

Marcus Denker-4
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

Reply | Threaded
Open this post in threaded view
|

Re: Fun with AST evaluation

Clément Béra
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 <[hidden email]> 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




--
Reply | Threaded
Open this post in threaded view
|

Re: Fun with AST evaluation

Stephane Ducasse-3
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 <[hidden email]> 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 <[hidden email]> 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




--

Reply | Threaded
Open this post in threaded view
|

Re: Fun with AST evaluation

Marcus Denker-4


> On 18 Apr 2018, at 07:41, Stephane Ducasse <[hidden email]> 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
Reply | Threaded
Open this post in threaded view
|

Re: Fun with AST evaluation

Marcus Denker-4


> On 18 Apr 2018, at 08:11, Marcus Denker <[hidden email]> wrote:
>
>
>
>> On 18 Apr 2018, at 07:41, Stephane Ducasse <[hidden email]> 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
Reply | Threaded
Open this post in threaded view
|

Re: Fun with AST evaluation

Sven Van Caekenberghe-2


> On 18 Apr 2018, at 08:34, Marcus Denker <[hidden email]> wrote:
>
>
>
>> On 18 Apr 2018, at 08:11, Marcus Denker <[hidden email]> wrote:
>>
>>
>>
>>> On 18 Apr 2018, at 07:41, Stephane Ducasse <[hidden email]> 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.


Reply | Threaded
Open this post in threaded view
|

Re: Fun with AST evaluation

Denis Kudriashov
In reply to this post by Stephane Ducasse-3

2018-04-18 7:41 GMT+02:00 Stephane Ducasse <[hidden email]>:
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 <[hidden email]> 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 <[hidden email]> 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




--


Reply | Threaded
Open this post in threaded view
|

Re: Fun with AST evaluation

Marcus Denker-4
In reply to this post by Marcus Denker-4


On 17 Apr 2018, at 17:45, Marcus Denker <[hidden email]> 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:



Marcus