discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

parser change in 19.12 vs 19.05

A
adrianv
Sat, Mar 14, 2020 8:16 PM

I have noticed that there is a change in parsing between versions
2019.12.21.ai4163 and 2019.05.  I display it here in a simple form:

It the current version the following code is legal:

a=[2,2,3,4];
x = is_list(a) && let(L=len(a)) L > 3 ? 1 : 2;

In the 2019.12 version, the above code produces an error.  It seems that the
let() statement is not allowed in that location.

I can move it to the front:

x = let(L=len(a)) is_list(a) && L > 3 ? 1 : 2;

but of course then I get an error if a is not a list, "len() parameter could
not be converted".  Yes, I know in this case it's trivial to work around,
but maybe in more complicated cases, it's more of a nuisance to work around
this.

Is this an intentional change in syntax?  If so, why?

--
Sent from: http://forum.openscad.org/

I have noticed that there is a change in parsing between versions 2019.12.21.ai4163 and 2019.05. I display it here in a simple form: It the current version the following code is legal: a=[2,2,3,4]; x = is_list(a) && let(L=len(a)) L > 3 ? 1 : 2; In the 2019.12 version, the above code produces an error. It seems that the let() statement is not allowed in that location. I can move it to the front: x = let(L=len(a)) is_list(a) && L > 3 ? 1 : 2; but of course then I get an error if a is not a list, "len() parameter could not be converted". Yes, I know in this case it's trivial to work around, but maybe in more complicated cases, it's more of a nuisance to work around this. Is this an intentional change in syntax? If so, why? -- Sent from: http://forum.openscad.org/
P
Parkinbot
Sat, Mar 14, 2020 9:50 PM

Obviously you always can write:
a = [2,2,3,4];
x = is_list(a) && len(a) > 3 ? 1 : 2;

A backward compatible expression for is_list(a) is
a[0]!=undef

In general you can use any (Boolean) function call to replace len().

However, your special semantics can be simply written as
y = a[3]!=undef ? 1 : 2;

--
Sent from: http://forum.openscad.org/

Obviously you always can write: a = [2,2,3,4]; x = is_list(a) && len(a) > 3 ? 1 : 2; A backward compatible expression for is_list(a) is a[0]!=undef In general you can use any (Boolean) function call to replace len(). However, your special semantics can be simply written as y = a[3]!=undef ? 1 : 2; -- Sent from: http://forum.openscad.org/
A
adrianv
Sat, Mar 14, 2020 10:36 PM

The point is not to find a workaround in this toy example, which is the
simplest example I could come up with to display the behavior.  Maybe other
situations in real code are not so trivial.  Yes, you can always work around
it somehow, perhaps with a bunch of temporary variables or more complex
repeated conditions.

The point is that the syntax has changed.  Is it intentional?  Why did it
change?

Parkinbot wrote

Obviously you always can write:
a = [2,2,3,4];
x = is_list(a) && len(a) > 3 ? 1 : 2;

A backward compatible expression for is_list(a) is
a[0]!=undef

In general you can use any (Boolean) function call to replace len().

However, your special semantics can be simply written as
y = a[3]!=undef ? 1 : 2;

--
Sent from: http://forum.openscad.org/


OpenSCAD mailing list

Discuss@.openscad

The point is not to find a workaround in this toy example, which is the simplest example I could come up with to display the behavior. Maybe other situations in real code are not so trivial. Yes, you can always work around it somehow, perhaps with a bunch of temporary variables or more complex repeated conditions. The point is that the syntax has changed. Is it intentional? Why did it change? Parkinbot wrote > Obviously you always can write: > a = [2,2,3,4]; > x = is_list(a) && len(a) > 3 ? 1 : 2; > > A backward compatible expression for is_list(a) is > a[0]!=undef > > In general you can use any (Boolean) function call to replace len(). > > However, your special semantics can be simply written as > y = a[3]!=undef ? 1 : 2; > > > > > > > -- > Sent from: http://forum.openscad.org/ > > _______________________________________________ > OpenSCAD mailing list > Discuss@.openscad > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org -- Sent from: http://forum.openscad.org/
P
Parkinbot
Sat, Mar 14, 2020 11:12 PM

I wouldn't ever expect to be able to define a variable within a boolean
expression - last but not least, because this is bad programming style. And
I don't regard it as a workaround to call a predicate function that does
intermediate calculations by use of local variables within its own body.

So what was the real intention that led to your question? Would you opt to
allow such freaky possiblities?
E.g.: While the compiler allows you to write
a=[2,2,let(x=10) 3,4];

without a warning, this syntax is obviously not intended, because the value
of x is not accessible.

b=[2,2,let(x=10) 3,4+y];

--
Sent from: http://forum.openscad.org/

I wouldn't ever expect to be able to define a variable within a boolean expression - last but not least, because this is bad programming style. And I don't regard it as a workaround to call a predicate function that does intermediate calculations by use of local variables within its own body. So what was the real intention that led to your question? Would you opt to allow such freaky possiblities? E.g.: While the compiler allows you to write a=[2,2,let(x=10) 3,4]; without a warning, this syntax is obviously not intended, because the value of x is not accessible. b=[2,2,let(x=10) 3,4+y]; -- Sent from: http://forum.openscad.org/
RP
Ronaldo Persiano
Sat, Mar 14, 2020 11:29 PM

It the current version the following code is legal:

a=[2,2,3,4];
x = is_list(a) && let(L=len(a)) L > 3 ? 1 : 2;

In the 2019.12 version, the above code produces an error.  It seems that
the
let() statement is not allowed in that location.

That seems really a parser change and I don't know why. However I found a
way to get the same semantics with
a minimal change that is easily applicable to more complex cases:

a=[2,2,3,4];
x = is_list(a) && ( let(L=len(a)) L > 3 ? 1 : 2 ) ;

or

a=[2,2,3,4];
x = is_list(a) && ( let(L=len(a)) L > 3 )? 1 : 2;

It the current version the following code is legal: > > a=[2,2,3,4]; > x = is_list(a) && let(L=len(a)) L > 3 ? 1 : 2; > > In the 2019.12 version, the above code produces an error. It seems that > the > let() statement is not allowed in that location. > That seems really a parser change and I don't know why. However I found a way to get the same semantics with a minimal change that is easily applicable to more complex cases: a=[2,2,3,4]; x = is_list(a) && ( let(L=len(a)) L > 3 ? 1 : 2 ) ; or a=[2,2,3,4]; x = is_list(a) && ( let(L=len(a)) L > 3 )? 1 : 2;
A
adrianv
Sun, Mar 15, 2020 1:15 AM

To me, nearly all uses of let() in OpenSCAD are unexpected and weird.  I have
gotten used to it.

I have no objection to your example, which I have modified to add a
dependence on x:

a=[2,2,let(x=10) 3*x,4];

You write

b=[2,2,let(x=10) 3,4+x];

and complain that x is not accessible.  Why would it be?  Its scope only
extends to the comma, so it's out of scope at the next list entry where you
try to use it.  Basically the behavior as I observe it is that any
expression

expr

can be replaced by

let(expr1) expr

Hence you can replace "3" by "let(x=4) 3*x" in the list.

a=[2,2,let(x=10) 3*x,4];

There is no reason to expect the scope of the let() to extend to the end of
the list, say.  If you wanted that you need to put the let() before the list
definition begins, as in

a = let(x=10) [2,3,3*x, 4+x];

This to me is pretty strange syntax---defining variables within definitions
of other variables--but it's how one must do things in OpenSCAD.  I don't
find this any less strang than the previous example where the let() was
inside the list.

With regards to workarounds, it does appear that adding parentheses works,
so my original example can be rewritten like this:

x = is_list(a) && (let(L=len(a)) L > 3) ? 1 : 2;

But note that it is not correct to write it as

x = is_list(a) && (let(L=len(a)) L > 3 ? 1 : 2);

which treats the whole second section just as a boolean and results in a
boolean value of x instead of either 1 or 2.

Parkinbot wrote

I wouldn't ever expect to be able to define a variable within a boolean
expression - last but not least, because this is bad programming style.
And
I don't regard it as a workaround to call a predicate function that does
intermediate calculations by use of local variables within its own body.

So what was the real intention that led to your question? Would you opt to
allow such freaky possiblities?
E.g.: While the compiler allows you to write
a=[2,2,let(x=10) 3,4];

without a warning, this syntax is obviously not intended, because the
value
of x is not accessible.

b=[2,2,let(x=10) 3,4+x];

--
Sent from: http://forum.openscad.org/


OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org

To me, nearly all uses of let() in OpenSCAD are unexpected and weird. I have gotten used to it. I have no objection to your example, which I have modified to add a dependence on x: a=[2,2,let(x=10) 3*x,4]; You write b=[2,2,let(x=10) 3,4+x]; and complain that x is not accessible. Why would it be? Its scope only extends to the comma, so it's out of scope at the next list entry where you try to use it. Basically the behavior as I observe it is that any expression expr can be replaced by let(expr1) expr Hence you can replace "3" by "let(x=4) 3*x" in the list. a=[2,2,let(x=10) 3*x,4]; There is no reason to expect the scope of the let() to extend to the end of the list, say. If you wanted that you need to put the let() before the list definition begins, as in a = let(x=10) [2,3,3*x, 4+x]; This to me is pretty strange syntax---defining variables within definitions of other variables--but it's how one must do things in OpenSCAD. I don't find this any less strang than the previous example where the let() was inside the list. With regards to workarounds, it does appear that adding parentheses works, so my original example can be rewritten like this: x = is_list(a) && (let(L=len(a)) L > 3) ? 1 : 2; But note that it is *not* correct to write it as x = is_list(a) && (let(L=len(a)) L > 3 ? 1 : 2); which treats the whole second section just as a boolean and results in a boolean value of x instead of either 1 or 2. Parkinbot wrote > I wouldn't ever expect to be able to define a variable within a boolean > expression - last but not least, because this is bad programming style. > And > I don't regard it as a workaround to call a predicate function that does > intermediate calculations by use of local variables within its own body. > > So what was the real intention that led to your question? Would you opt to > allow such freaky possiblities? > E.g.: While the compiler allows you to write > a=[2,2,let(x=10) 3,4]; > > without a warning, this syntax is obviously not intended, because the > value > of x is not accessible. > > b=[2,2,let(x=10) 3,4+x]; > > > > > -- > Sent from: http://forum.openscad.org/ > > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org -- Sent from: http://forum.openscad.org/
DM
Doug Moen
Sun, Mar 15, 2020 1:52 AM

adrianv wrote:

The point is that the syntax has changed.  Is it intentional?  Why did it
change?

Based on my experience with parsers and programming language syntax, the new behaviour is what I expect, and the old behaviour is unexpected.

It is a matter of operator precedence. The && operator has higher precedence than the let operator, so I would not expect that a let expression could be parsed as a legal argument to &&. Therefore, I would expect that the let expression would need to be parenthesized in your example.

I looked at the code, and the parser (a Bison program called src/parser.y) has been rewritten from the former style, which relied on Bison operator precedence declarations, to a new more explicit style that defines a separate non-terminal for each level of operator precedence in an expression. I endorse the new coding style. Previously, the behaviour was "whatever Bison does", which is hard to infer from the source code. Now, the grammar is explicit, and you can see exactly how the arguments to && will be parsed.

So I don't think that the change in behaviour was intentional. I think it was the result of replacing unspecified, implementation defined parsing behaviour with an explicit grammar where we know how things will be parsed.

adrianv wrote: > The point is that the syntax has changed. Is it intentional? Why did it > change? Based on my experience with parsers and programming language syntax, the new behaviour is what I expect, and the old behaviour is unexpected. It is a matter of operator precedence. The `&&` operator has higher precedence than the `let` operator, so I would not expect that a `let` expression could be parsed as a legal argument to `&&`. Therefore, I would expect that the `let` expression would need to be parenthesized in your example. I looked at the code, and the parser (a Bison program called src/parser.y) has been rewritten from the former style, which relied on Bison operator precedence declarations, to a new more explicit style that defines a separate non-terminal for each level of operator precedence in an expression. I endorse the new coding style. Previously, the behaviour was "whatever Bison does", which is hard to infer from the source code. Now, the grammar is explicit, and you can see exactly how the arguments to `&&` will be parsed. So I don't think that the change in behaviour was intentional. I think it was the result of replacing unspecified, implementation defined parsing behaviour with an explicit grammar where we know how things will be parsed.