ASM: Execution can fall off end of the code

classic Classic list List threaded Threaded
20 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

ASM: Execution can fall off end of the code

sulfinu
I obtain this stack trace when trying to instantiate a complex, real-life parser:

Exception in thread "main" java.lang.RuntimeException: Error creating extended parser class: Execution can fall off end of the code
        at org.parboiled.Parboiled.createParser(Parboiled.java:58)
        at ParserPlayground.main(ParserPlayground.java:103)
Caused by: org.objectweb.asm.tree.analysis.AnalyzerException: Execution can fall off end of the code
        at org.objectweb.asm.tree.analysis.Analyzer.findSubroutine(Unknown Source)
        at org.objectweb.asm.tree.analysis.Analyzer.findSubroutine(Unknown Source)
        at org.objectweb.asm.tree.analysis.Analyzer.analyze(Unknown Source)
        at org.parboiled.transform.InstructionGraphCreator.process(InstructionGraphCreator.java:43)
        at org.parboiled.transform.ParserTransformer.runMethodTransformers(ParserTransformer.java:62)
        at org.parboiled.transform.ParserTransformer.extendParserClass(ParserTransformer.java:45)
        at org.parboiled.transform.ParserTransformer.transformParser(ParserTransformer.java:38)
        at org.parboiled.Parboiled.createParser(Parboiled.java:54)
        ... 1 more

Line 43 in InstructionGraphCreator makes a call to the Analyzer.analyze() for a method that goes like this:

@Cached
Rule VALUE(ValueType type)
        {
                switch (type)
                {
                        case BINARY:
                                return Sequence(...);
   case BOOLEAN:
                                return Sequence(...);
    .
    .
    .
    case UNKNOWN:
                                 return Sequence(...);
   }
   return null;  // never reached
}

Implicit and explicit actions are used in various branches of "switch". I have also tried to use the "default" branch, without the final "return null" line (actually, this was the initial version).
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ASM: Execution can fall off end of the code

sulfinu
I have transformed the "switch" statement into a series of "if"s and I got past that. But I've hit another one :(

Exception in thread "main" java.lang.NullPointerException
        at Parser$$parboiled.PROPERTY(Unknown Source)
        at Parser$$parboiled.COMPONENT(Unknown Source)
        at Parser.STREAM(Parser.java:583)
        at Parser$$parboiled.STREAM(Unknown Source)

produced by this statement:
new ReportingParseRunner(parser.STREAM())

The methods with uppercase names are rules (i.e. they return a Rule). I have inserted an action with a breakpoint on it, but apparently actions are not executed during this phase, before actually analyzing an input string.

I'm lost here, unfortunately...
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ASM: Execution can fall off end of the code

mathias
Administrator
In reply to this post by sulfinu
Hmm... this is definitely another bug in the parser extension.
You really seem to exercise it in a way other users haven't so far... :)

I'll have to look into this as well.
If you change to method to have only one "return" statement it should work fine (e.g. by using "break" statements, rather than "returns").

Cheers,
Mathias

---
[hidden email]
http://www.parboiled.org

On 28.07.2011, at 20:54, sulfinu [via parboiled users] wrote:

> I obtain this stack trace when trying to instantiate a complex, real-life parser:
>
> Exception in thread "main" java.lang.RuntimeException: Error creating extended parser class: Execution can fall off end of the code
>         at org.parboiled.Parboiled.createParser(Parboiled.java:58)
>         at ParserPlayground.main(ParserPlayground.java:103)
> Caused by: org.objectweb.asm.tree.analysis.AnalyzerException: Execution can fall off end of the code
>         at org.objectweb.asm.tree.analysis.Analyzer.findSubroutine(Unknown Source)
>         at org.objectweb.asm.tree.analysis.Analyzer.findSubroutine(Unknown Source)
>         at org.objectweb.asm.tree.analysis.Analyzer.analyze(Unknown Source)
>         at org.parboiled.transform.InstructionGraphCreator.process(InstructionGraphCreator.java:43)
>         at org.parboiled.transform.ParserTransformer.runMethodTransformers(ParserTransformer.java:62)
>         at org.parboiled.transform.ParserTransformer.extendParserClass(ParserTransformer.java:45)
>         at org.parboiled.transform.ParserTransformer.transformParser(ParserTransformer.java:38)
>         at org.parboiled.Parboiled.createParser(Parboiled.java:54)
>         ... 1 more
>
> Line 43 in InstructionGraphCreator makes a call to the Analyzer.analyze() for a method that goes like this:
>
> @Cached
> Rule VALUE(ValueType type)
>         {
>                 switch (type)
>                 {
>                         case BINARY:
>                                 return Sequence(...);
>    case BOOLEAN:
>                                 return Sequence(...);
>     .
>     .
>     .
>     case UNKNOWN:
>                                  return Sequence(...);
>    }
>    return null;  // never reached
> }
>
> Implicit and explicit actions are used in various branches of "switch". I have also tried to use the "default" branch, without the final "return null" line (actually, this was the initial version).
>
>
> If you reply to this email, your message will be added to the discussion below:
> http://users.parboiled.org/ASM-Execution-can-fall-off-end-of-the-code-tp3207484p3207484.html
> To start a new topic under parboiled users, email [hidden email]
> To unsubscribe from parboiled users, click here.

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ASM: Execution can fall off end of the code

mathias
Administrator
In reply to this post by sulfinu
Can you post your PROPERTY rule?

---
[hidden email]
http://www.parboiled.org

On 29.07.2011, at 10:08, sulfinu [via parboiled users] wrote:

> I have transformed the "switch" statement into a series of "if"s and I got passed that. But I've hit another one :(
>
> Exception in thread "main" java.lang.NullPointerException
>         at Parser$$parboiled.PROPERTY(Unknown Source)
>         at Parser$$parboiled.COMPONENT(Unknown Source)
>         at Parser.STREAM(Parser.java:583)
>         at Parser$$parboiled.STREAM(Unknown Source)
>
> produced by this statement:
> new ReportingParseRunner(parser.STREAM())
>
> The methods with uppercase names are rules (i.e. they return a Rule). I have inserted an action with a breakpoint on it, but apparently actions are not executed during this phase, before actually analyzing an input string.
>
> I'm lost here, unfortunately...
>
> If you reply to this email, your message will be added to the discussion below:
> http://users.parboiled.org/ASM-Execution-can-fall-off-end-of-the-code-tp3207484p3208944.html
> To start a new topic under parboiled users, email [hidden email]
> To unsubscribe from parboiled users, click here.

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ASM: Execution can fall off end of the code

sulfinu
Well, I'm trying to employ Parboiled in a real-life proprietary software. And I have the habit of discovering bugs, true...
By the way, I also discovered that if someone names a rule method with an initial '$',  that method is inadvertently treated because the '$'-prefix is a mark of instrumented methods (a note in the documentation, perhaps?).

The bothering rule recognizes a property which might have several parameters and also has a value. It looks like this (edited as well for anonymity):

        Rule PROPERTY()
        {
                return Sequence(
                                VALUE(PropertyType.class),  //  the VALUE() rule is overloaded, this variant takes a Class as parameter, while the first one takes an enumeration value as parameter
                                ACTION(support.createProperty()),
                                ZeroOrMore(FirstOf(
                                                Sequence(ACTION(support.noParameterOfType(ParameterType.ALT)), ALT_PARAMETER()),
                                                Sequence(ACTION(support.noParameterOfType(ParameterType.CN)), CN_PARAMETER()),
      // .
      // and others alike
      // .
                                                UNRECOGNIZED_PARAMETER()
                                                )),
                                Ch(':'),
                                VALUE(currentProperty.getValueType()),  // this is an invocation of the first variant of VALUE()
                                String("\r\n"),
                                ACTION(support.assignProperty(true))
                );
        }

"currentProperty" is a member field of Parser
"support" is another member field of Parser, having as type an inner class implementing ContextAware

I hope you can guess what is wrong. Thanks for your help.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ASM: Execution can fall off end of the code

sulfinu
I must add that it is not the method overloading that triggers the bug.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ASM: Execution can fall off end of the code

sulfinu
In reply to this post by mathias
Having exhausted other options, I also tried what you suggested with my "switch" statement in the VALUE() rule, namely saving the returned result in a local variable in every "case", and then using a single "return" statement at the end. It failed with the same message:

Exception in thread "main" java.lang.RuntimeException: Error creating extended parser class: Execution can fall off end of the code
        at org.parboiled.Parboiled.createParser(Parboiled.java:58)
        at ParserPlayground.main(ParserPlayground.java:109)
Caused by: org.objectweb.asm.tree.analysis.AnalyzerException: Execution can fall off end of the code
        at org.objectweb.asm.tree.analysis.Analyzer.findSubroutine(Unknown Source)
        at org.objectweb.asm.tree.analysis.Analyzer.findSubroutine(Unknown Source)
        at org.objectweb.asm.tree.analysis.Analyzer.analyze(Unknown Source)
        at org.parboiled.transform.InstructionGraphCreator.process(InstructionGraphCreator.java:43)
        at org.parboiled.transform.ParserTransformer.runMethodTransformers(ParserTransformer.java:62)
        at org.parboiled.transform.ParserTransformer.extendParserClass(ParserTransformer.java:45)
        at org.parboiled.transform.ParserTransformer.transformParser(ParserTransformer.java:38)
        at org.parboiled.Parboiled.createParser(Parboiled.java:54)
        ... 1 more


I desperately need help here since I'm also running out of time... Thanks.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ASM: Execution can fall off end of the code

mathias
Administrator
In reply to this post by sulfinu
>  //  the VALUE() rule is overloaded, this variant takes a Class as parameter, while the first one takes an enumeration value as parameter

Can you try removing the overloading and renaming one of the two VALUE methods?

---
[hidden email]
http://www.parboiled.org

On 29.07.2011, at 13:14, sulfinu [via parboiled users] wrote:

> Well, I'm trying to employ Parboiled in a real-life proprietary software. And I have the habit of discovering bugs, true...
> By the way, I also discovered that if someone names a rule method with an initial '$',  that method is inadvertently treated because the '$'-prefix is a mark of instrumented methods (a note in the documentation, perhaps?).
>
> The bothering rule recognizes a property which might have several parameters and also has a value. It looks like this (edited as well for anonymity):
>
>         Rule PROPERTY()
>         {
>                 return Sequence(
>                                 VALUE(PropertyType.class),  //  the VALUE() rule is overloaded, this variant takes a Class as parameter, while the first one takes an enumeration value as parameter
>                                 ACTION(support.createProperty()),
>                                 ZeroOrMore(FirstOf(
>                                                 Sequence(ACTION(support.noParameterOfType(ParameterType.ALT)), ALT_PARAMETER()),
>                                                 Sequence(ACTION(support.noParameterOfType(ParameterType.CN)), CN_PARAMETER()),
>       // .
>       // and others alike
>       // .
>                                                 UNRECOGNIZED_PARAMETER()
>                                                 )),
>                                 Ch(':'),
>                                 VALUE(currentProperty.getValueType()),  // this is an invocation of the first variant of VALUE()
>                                 String("\r\n"),
>                                 ACTION(support.assignProperty(true))
>                 );
>         }
>
> "currentProperty" is a member field of Parser
> "support" is an inner class implementing ContextAware
>
> I hope you can guess what is wrong. Thanks for your help.
>
> If you reply to this email, your message will be added to the discussion below:
> http://users.parboiled.org/ASM-Execution-can-fall-off-end-of-the-code-tp3207484p3209327.html
> To start a new topic under parboiled users, email [hidden email]
> To unsubscribe from parboiled users, click here.

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ASM: Execution can fall off end of the code

sulfinu
Already tried that, naturally. See the 6th comment in this thread.
...Welcome back :)
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ASM: Execution can fall off end of the code

mathias
Administrator
> ...Welcome back :)

Thanks... :)
I was on vacation for a week...

One more thing:
Why are you using the explicit ACTION wrappers rather than relying on implicit action wrapping?

Cheers,
Mathias

---
[hidden email]
http://www.parboiled.org

On 09.08.2011, at 17:01, sulfinu [via parboiled users] wrote:

> Already tried that, naturally. See the 6th comment in this thread.
> ...Welcome back :)
>
> If you reply to this email, your message will be added to the discussion below:
> http://users.parboiled.org/ASM-Execution-can-fall-off-end-of-the-code-tp3207484p3239251.html
> To start a new topic under parboiled users, email [hidden email]
> To unsubscribe from parboiled users, click here.

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ASM: Execution can fall off end of the code

sulfinu
Because it looks cleaner. Saving as many characters as 8 against a much longer boolean expression is not worth it.
Take it as my taste; we have got a saying here: "tastes are not to be debated" (approximate translation).
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ASM: Execution can fall off end of the code

mathias
Administrator
No problem.
Just wanted to know whether maybe there was a technical reason, e.g. implicit action wrapping did not work for you.

Cheers,
Mathias

---
[hidden email]
http://www.parboiled.org

On 10.08.2011, at 09:32, sulfinu [via parboiled users] wrote:

> Because it looks cleaner. Saving as many characters as 8 against a much longer boolean expression is not worth it.
> Take it as my taste; we have got a saying here: "tastes are not to be debated" (approximate translation).
>
> If you reply to this email, your message will be added to the discussion below:
> http://users.parboiled.org/ASM-Execution-can-fall-off-end-of-the-code-tp3207484p3241663.html
> To start a new topic under parboiled users, email [hidden email]
> To unsubscribe from parboiled users, click here.

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ASM: Execution can fall off end of the code

sulfinu
I have sent you an email at "decodified.com", could you please check that it hasn't ended up in the spam folder?
Thanks.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ASM: Execution can fall off end of the code

mathias
Administrator
Sulfinu,

I am currently (once more) on vacation and won't really have time to look at this issue until next thursday.
I understand that errors in the parboiled parser extender are really tricky to debug.

I suggest you try to change the failing methods in some way that doesn't really alter your parser logic but simplifies the rule method code. Parboiled performs a dataflow analysis on the rule methods byte code, I assume the bug is somewhere there. I don't really recommend you try to find the bug yourself, since this will likely cost you too much time. Try to move code out of your rule method in other helper methods (not returning rules), try to disable caching (@DontCache) or even all extension (@DontExtend) for single rule methods and generally play around a bit.
parboiled has proven to be quite solid in a lot of applications, so I am sure there is way to write your parser in a way that doesn't trigger the bug.

If all this doesn't work try to distill a minimal failing test case that I can use to home in on the bug and produce a fix ASAP.

Cheers,
Mathias

---
[hidden email]
http://www.parboiled.org

On 12.08.2011, at 18:16, sulfinu [via parboiled users] wrote:

> I have sent you an email at "decodified.com", could you please check that it hasn't ended up in the spam folder?
> Thanks.
>
> If you reply to this email, your message will be added to the discussion below:
> http://users.parboiled.org/ASM-Execution-can-fall-off-end-of-the-code-tp3207484p3249700.html
> To start a new topic under parboiled users, email [hidden email]
> To unsubscribe from parboiled users, click here.

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ASM: Execution can fall off end of the code

sulfinu
This post was updated on .
First of all, I sincerely wish you a beautiful vacation!
In the meantime, I'll test your tips.

...Which I did, and now I am posting the "result".
I tried to replicate in a small test parser the things I came to consider problematic in my particular parser with respect to Parboiled instrumentation. And I found something totally unexpected: the rule methods with parameters (remember them?) are evaluated during the "pre-processing" phase, while they shouldn't!
"Why?", you may ask. Well, because their actual parameters are not assigned before the actual parsing, since that takes place in actions' code, depending on the submitted input.
This is the reason for the NullPointerException I've been obtaining after managing to get past the improperly handled "switch" statement. The actual parameters for parameterized rules are not (intentionally) initialized, yet, at this stage.

What's even worse is that the fake result of a parameterized rule obtained during the instrumentation phase, is kept and used from that point on in the parent rule, regardless of the actual parameters' values (which fluctuate during parsing). Obviously, this renders the parser useless... The @Cached annotation has no effect on this behaviour.

Granted, dynamic rules are not by themselves part of a PEG. But they would have been a welcome shortcut for repeated trees (graphs) of rules with small differences among them... :'(

Here is my test playground:

public class ParserPlayground extends BaseParser
{
        public Support support = new Support();
        String retained;
        SomeType wanted;
       
        enum SomeType
        {
                X, Y, Z
        }
       
        protected class Support implements ContextAware
        {
                Context context;
               
                @Override
                public void setContext(Context context)
                {
                        this.context = context;
                }
               
                public boolean same()
                {
                        if (! retained.equals(context.getMatch()))
                                throw new ActionException("They do NOT match!");
                        return true;
                }
        }
       
        Rule ALL()
        {
                return Sequence(OneOrMore(LINE()), EOI);
        }
       
        Rule LINE()
        {
                return Sequence(
                                VALUE(SomeType.Z),
                                new Action() {
                                        @Override
                                        public boolean run(Context context)
                                        {
                                                retained = context.getMatch();
                                                wanted = SomeType.Z;
                                                return true;
                                        }
                                },
                                Ch(':'),
                                VALUE(wanted),
                                ACTION(support.same()),
                                Ch('\n')
                );
        }
       
        Rule VALUE(SomeType type)
        {
                if (type != null)
                        switch (type)
                        {
                                case X:
                                        return Sequence(
                                                        Ch('x'),
                                                        ZeroOrMore(ALPHA())
                                        );
                                case Y:
                                        return Sequence(
                                                        Ch('y'),
                                                        ZeroOrMore(ALPHA())
                                        );
                                case Z:
                                        return ZeroOrMore(ALPHA());
                        }
                return Ch('a');  // should not happen in real life, but it does, during pre-processing!
        }
       
        Rule ALPHA()
        {
                return CharRange('a', 'z');
        }
       
        public static void main(String[] args) throws IOException
        {
                ParserPlayground parser = Parboiled.createParser(ParserPlayground.class);
                ParseRunner runner = new BasicParseRunner(parser.ALL());
                ParsingResult result = runner.run("aa:aa\nbb:bb\n");
                if (! result.matched)
                        if (result.hasErrors())
                                throw new ParsingException(ErrorUtils.printParseErrors(result));
                        else throw new ParsingException();
 }
}
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ASM: Execution can fall off end of the code

sulfinu
This post was updated on .
So, did you manage to look over the "switch" with multiple returns issue? I've tried different variants, you can review the first few messages in this thread.

A week ago I have implemented a workaround for the eager evaluation of rule parameters problem. It is ugly, convoluted, but works :)
Instead of calling directly the parameterized rule like this:

Sequence(
...
VALUE(someField.someMethod()),
...
)

I make use of the deferred evaluation of actions (well, what do you know :) ) to decouple the parameterized call from the "preprocessed" graph of rules:


Sequence(
...
new Action(){
 public boolean run(Context context)
 {
  new BaseParseRunner(MyParser.this.VALUE(someField.someMethod())).run(<shifted input buffer>);
 }
},
Sequence(...),  // consume the characters parsed above
...
)

Naturally, it is necessary to consume the characters matched in the parameterized rule in a subsequent rule. Hence, my question in another thread about actions that must instruct the ParseRunner to skip some input characters.
Obviously, the above lines of code are just the solution skeleton, my real code is slighty more complicated, which gave me the opportunity to discover something else about the Parboiled instrumentation process:
If someone calls a parameterized rule R() from another parameterized rule P() with an actual parameter equal a the formal parameter of P(), things work as expected, since the eager evaluation of the actual parameter is not possible anymore ;)

Once again, thank you for Parboiled, I know how demanding can be working on such a project. But it's your merit that Parboiled users get so demanding after tasting it and finding it so appealing...
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ASM: Execution can fall off end of the code

mathias
Administrator
I will definitely take a look at this issue before the next release.

Thanks again for the report and cheers,
Mathias

---
[hidden email]
http://www.parboiled.org

On 12.09.2011, at 11:59, sulfinu [via parboiled users] wrote:

> So, did you manage to look over the "switch" with multiple returns issue? I've tried different variants, you can review the first few messages in this thread.
>
> A week ago I have implemented a workaround for the eager evaluation of rule parameters problem. It is ugly, convoluted, but works :)
> Instead of calling directy the parameterized rule like this:
>
> Sequence(
> ...
> VALUE(someField.someMethod()),
> ...
> )
>
> I make use of the deferred evaluation of actions (well, what do you know :) ) to decouple the parameterized call from the "preprocessed" graph of rules:
>
>
> Sequence(
> ...
> new Action(){
>  public boolean run(Context context)
>  {
>   new BaseParseRunner(MyParser.this.VALUE(someField.someMethod())).run(<shifted input buffer>);
>  }
> },
> Sequence(...),  // consume the characters parsed above
> ...
> )
>
> Naturally, it is necesary to consume the characters matched in the parameterized rule in a subsequent rule. Hence, my question in another thread about actions that must instruct the ParseRunner to skip some input characters.
> Obviously, the above lines of code are just the solution skeleton, my real code is slighty more complicated, which gave me the opportunity to discover something else about the Parboiled instrumentation process:
> If someone calls a parameterized rule R() from another parameterized rule P() with an actual parameter equal a the formal parameter of P(), things work as expected, since the eager evaluation of the actual parameter is not possible anymore ;)
>
> Once again, thank you for Parboiled, I know how demanding can be working on such a project. But it's your merit that Parboiled users get so demanding after tasting it and finding it so appealing...
>
> If you reply to this email, your message will be added to the discussion below:
> http://users.parboiled.org/ASM-Execution-can-fall-off-end-of-the-code-tp3207484p3329268.html
> To start a new topic under parboiled users, email [hidden email]
> To unsubscribe from parboiled users, click here.

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ASM: Execution can fall off end of the code

sulfinu
I can't help asking which of the issues are you referring to: the "switch" with multiple return statements or the much ampler problem of eager evaluation of parameterized rules?
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ASM: Execution can fall off end of the code

mathias
Administrator
Sulfino,

I'm talking about a fix to issue #31: https://github.com/sirthias/parboiled/issues/31.

I'm not exactly sure what you mean by the "eager evaluation of parameterized rules" problem.
Parameters to a rule method can be of two kinds:
1. "Regular" Java parameters are evaluated during the rule construction phase and can be used to parameterized rule _construction_.
2. Action variables, when passed to rule methods as arguments, serve as containers that allow you to transport state between rules during rule _execution_.

So you do have two options for parameterizing rules that should cover most use cases…

What exactly is your use case?

Cheers,
Mathias

---
[hidden email]
http://www.parboiled.org

On 13.09.2011, at 12:23, sulfinu [via parboiled users] wrote:

> I can't help asking which of the issues are you referring to: the "switch" with multiple return statements or the much ampler problem of eager evaluation of parameterized rules?
>
> If you reply to this email, your message will be added to the discussion below:
> http://users.parboiled.org/ASM-Execution-can-fall-off-end-of-the-code-tp3207484p3332239.html
> To start a new topic under parboiled users, email [hidden email]
> To unsubscribe from parboiled users, click here.

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: ASM: Execution can fall off end of the code

sulfinu
My reply comes late as I haven't visited this forum in a while. You have a full example of my "use case" in a comment I posted in this thread on August 16th. I thought the text was clear enough.

Look at the second invocation of VALUE() in the LINE() rule. Don't try to rewrite anything, just to make it work as this silly example is written.
I must admit that Vars were not appealing for me at all at the time I read the manual.

Off topic, my nickname ends in 'u', not 'o' (which is a feminine suffix).
Loading...