digitalmars.D - context-free grammar
- Simon Buerger (13/13) Mar 04 2011 It is often said that D's grammar is easier to parse than C++, i.e. it
- Simen kjaeraas (6/18) Mar 04 2011 Well, obviously not. The grammar has one and only one meaning for that
- bearophile (37/40) Mar 04 2011 This little program:
- SiegeLord (13/61) Mar 04 2011 Yeah, and this:
- Walter Bright (4/8) Mar 04 2011 There isn't one.
- Jonathan M Davis (5/20) Mar 04 2011 Umm. How could a * b be assigned to? It's definitely not an lvalue. Do y...
- uri (2/22) Mar 04 2011 Explain why (a*b) is lvalue in bearophile's second example. This is one ...
- Jonathan M Davis (9/34) Mar 04 2011 I'd argue that it's a bug related to operator overloading. = is converte...
- Walter Bright (4/7) Mar 04 2011 It's not a weird corner case at all. Temporaries can be used as lvalues ...
- Jonathan M Davis (6/16) Mar 04 2011 Really? I thought that a temporary was pretty much _the_ classic example...
- Peter Alexander (6/22) Mar 05 2011 How do you think array assignments in C++ work?
- Mafi (7/35) Mar 05 2011 No, the temporary in this case is not an lvalue. It's an adress whose
- Peter Alexander (18/31) Mar 05 2011 (a + i) is an address (type T*)
- Mafi (36/57) Mar 05 2011 I know. I meant the temporary itself (ie a + i) is not an lvalue; it
- %u (8/8) Mar 06 2011 I didn't see this example being mentioned in this thread (although I
- Jonathan M Davis (11/19) Mar 06 2011 That's essentially the example that's been under discussion - though in ...
- %u (8/10) Mar 06 2011 a temporary for the lvalue. Regardless, it's context free because a * b ...
- Jonathan M Davis (11/25) Mar 06 2011 It's not really that multiplication is being special-cased. It's that wh...
- KennyTM~ (23/33) Mar 06 2011 Only statement of the form 'p*q=r' is "special-cased". Actually it's not...
- Nick Sabalausky (5/31) Mar 04 2011 Corner cases are certainly a PITA in certain corner cases. But simplisti...
- Rainer Schuetze (5/22) Mar 04 2011 The ambiguities are simply resolved by this rule in the language
It is often said that D's grammar is easier to parse than C++, i.e. it should be possible to seperate syntactic and semantic analysis, which is not possible in C++ with the template-"< >" and so on. But I found following example: The Line "a * b = c;" can be interpreted in two ways: -> Declaration of variable b of type a* -> (a*b) is itself a lvalue which is assigned to. Current D (gdc 2.051) interprets it always in the first way and yields an error if the second is meant. The Workaround is simply to use parens like "(a*b)=c", so it's not a real issue. But at the same time, C++ (gcc 4.5) has no problem to distinguish it even without parens. So, is the advertising as "context-free grammar" wrong? - Krox
Mar 04 2011
Simon Buerger <krox gmx.net> wrote:It is often said that D's grammar is easier to parse than C++, i.e. it should be possible to seperate syntactic and semantic analysis, which is not possible in C++ with the template-"< >" and so on. But I found following example: The Line "a * b = c;" can be interpreted in two ways: -> Declaration of variable b of type a* -> (a*b) is itself a lvalue which is assigned to. Current D (gdc 2.051) interprets it always in the first way and yields an error if the second is meant. The Workaround is simply to use parens like "(a*b)=c", so it's not a real issue. But at the same time, C++ (gcc 4.5) has no problem to distinguish it even without parens. So, is the advertising as "context-free grammar" wrong?Well, obviously not. The grammar has one and only one meaning for that example - that of an a* called b, being set to c. This can be inferred with no other context. -- Simen
Mar 04 2011
Simen kjaeraas:Well, obviously not. The grammar has one and only one meaning for that example - that of an a* called b, being set to c. This can be inferred with no other context.This little program: struct Foo { int x; Foo opBinary(string op:"*")(Foo other) { Foo result = Foo(x * other.x); return result; } void opAssign(Foo other) { x = other.x; } } void main() { Foo a, b, c; a * b = c; } Gives: test.d(10): Error: a is used as a type test.d(10): Error: cannot implicitly convert expression (c) of type Foo to _error_* test.d(10): Error: declaration test.main.b is already defined While this one gives no errors: struct Foo { int x; Foo opBinary(string op:"*")(Foo other) { Foo result = Foo(x * other.x); return result; } void opAssign(Foo other) { x = other.x; } } void main() { Foo a, b, c; (a * b) = c; } Bye, bearophile
Mar 04 2011
bearophile Wrote:Simen kjaeraas:Yeah, and this: struct Foo { } void main() { Foo a, b, c; (a * b) = c; } Gives an error. I don't see any problem here: a * b; // always a pointer declaration (a * b); // always a binary expression -SiegeLordWell, obviously not. The grammar has one and only one meaning for that example - that of an a* called b, being set to c. This can be inferred with no other context.This little program: struct Foo { int x; Foo opBinary(string op:"*")(Foo other) { Foo result = Foo(x * other.x); return result; } void opAssign(Foo other) { x = other.x; } } void main() { Foo a, b, c; a * b = c; } Gives: test.d(10): Error: a is used as a type test.d(10): Error: cannot implicitly convert expression (c) of type Foo to _error_* test.d(10): Error: declaration test.main.b is already defined While this one gives no errors: struct Foo { int x; Foo opBinary(string op:"*")(Foo other) { Foo result = Foo(x * other.x); return result; } void opAssign(Foo other) { x = other.x; } } void main() { Foo a, b, c; (a * b) = c; } Bye, bearophile
Mar 04 2011
SiegeLord wrote:Gives an error. I don't see any problem here: a * b; // always a pointer declaration (a * b); // always a binary expressionThere isn't one. C++ decides if a*b=c; is a declaration or expression based on whether 'a' is a type or a variable. That requires semantic analysis. D's rule does not.
Mar 04 2011
On Friday 04 March 2011 17:05:57 Simon Buerger wrote:It is often said that D's grammar is easier to parse than C++, i.e. it should be possible to seperate syntactic and semantic analysis, which is not possible in C++ with the template-"< >" and so on. But I found following example: The Line "a * b = c;" can be interpreted in two ways: -> Declaration of variable b of type a* -> (a*b) is itself a lvalue which is assigned to. Current D (gdc 2.051) interprets it always in the first way and yields an error if the second is meant. The Workaround is simply to use parens like "(a*b)=c", so it's not a real issue. But at the same time, C++ (gcc 4.5) has no problem to distinguish it even without parens. So, is the advertising as "context-free grammar" wrong?Umm. How could a * b be assigned to? It's definitely not an lvalue. Do you mean that an overloaded opBinary!"*" is used which returns a ref? It certainly can't be done normally. - Jonathan M Davis
Mar 04 2011
Jonathan M Davis Wrote:On Friday 04 March 2011 17:05:57 Simon Buerger wrote:Explain why (a*b) is lvalue in bearophile's second example. This is one of the weird things in D. The language is too complex. It takes years to find out about the corner cases. I wouldn't use it for anything reliableIt is often said that D's grammar is easier to parse than C++, i.e. it should be possible to seperate syntactic and semantic analysis, which is not possible in C++ with the template-"< >" and so on. But I found following example: The Line "a * b = c;" can be interpreted in two ways: -> Declaration of variable b of type a* -> (a*b) is itself a lvalue which is assigned to. Current D (gdc 2.051) interprets it always in the first way and yields an error if the second is meant. The Workaround is simply to use parens like "(a*b)=c", so it's not a real issue. But at the same time, C++ (gcc 4.5) has no problem to distinguish it even without parens. So, is the advertising as "context-free grammar" wrong?Umm. How could a * b be assigned to? It's definitely not an lvalue. Do you mean that an overloaded opBinary!"*" is used which returns a ref? It certainly can't be done normally.
Mar 04 2011
On Friday 04 March 2011 19:10:35 uri wrote:Jonathan M Davis Wrote:I'd argue that it's a bug related to operator overloading. = is converted to opAssign, which is then essentially a normal function. And because the result of * is a Foo which has an overloaded opAssign, the call to opAssign succeeds like any other function call would, because = was lowered to opAssign. I don't see what else could possibly be happening here. And since this violates the fact that opAssign is supposed to operate on lvalues, and the result of a * b is an rvalue, not an lvalue, this is a bug. - Jonathan M DavisOn Friday 04 March 2011 17:05:57 Simon Buerger wrote:Explain why (a*b) is lvalue in bearophile's second example. This is one of the weird things in D. The language is too complex. It takes years to find out about the corner cases. I wouldn't use it for anything reliableIt is often said that D's grammar is easier to parse than C++, i.e. it should be possible to seperate syntactic and semantic analysis, which is not possible in C++ with the template-"< >" and so on. But I found following example: The Line "a * b = c;" can be interpreted in two ways: -> Declaration of variable b of type a* -> (a*b) is itself a lvalue which is assigned to. Current D (gdc 2.051) interprets it always in the first way and yields an error if the second is meant. The Workaround is simply to use parens like "(a*b)=c", so it's not a real issue. But at the same time, C++ (gcc 4.5) has no problem to distinguish it even without parens. So, is the advertising as "context-free grammar" wrong?Umm. How could a * b be assigned to? It's definitely not an lvalue. Do you mean that an overloaded opBinary!"*" is used which returns a ref? It certainly can't be done normally.
Mar 04 2011
uri wrote:Explain why (a*b) is lvalue in bearophile's second example.Because the expression evaluates to a temporary, which is an lvalue.This is one of the weird things in D. The language is too complex. It takes years to find out about the corner cases.It's not a weird corner case at all. Temporaries can be used as lvalues (in C++ too).
Mar 04 2011
On Friday 04 March 2011 20:31:38 Walter Bright wrote:uri wrote:Really? I thought that a temporary was pretty much _the_ classic example of an rvalue. If you can assign to temporaries, you can assign to most anything then, other than literals. Why on earth would assigning to temporaries be permitted? That just seems unnecessary and bug-prone. - Jonathan M DavisExplain why (a*b) is lvalue in bearophile's second example.Because the expression evaluates to a temporary, which is an lvalue.This is one of the weird things in D. The language is too complex. It takes years to find out about the corner cases.It's not a weird corner case at all. Temporaries can be used as lvalues (in C++ too).
Mar 04 2011
On 5/03/11 4:39 AM, Jonathan M Davis wrote:On Friday 04 March 2011 20:31:38 Walter Bright wrote:How do you think array assignments in C++ work? a[i] = x; a[i] is just *(a + i), i.e. the evaluation of an expression that yields a temporary, which in this case is an lvalue. Same applies to all other operator[] overloads.uri wrote:Really? I thought that a temporary was pretty much _the_ classic example of an rvalue. If you can assign to temporaries, you can assign to most anything then, other than literals. Why on earth would assigning to temporaries be permitted? That just seems unnecessary and bug-prone. - Jonathan M DavisExplain why (a*b) is lvalue in bearophile's second example.Because the expression evaluates to a temporary, which is an lvalue.This is one of the weird things in D. The language is too complex. It takes years to find out about the corner cases.It's not a weird corner case at all. Temporaries can be used as lvalues (in C++ too).
Mar 05 2011
Am 05.03.2011 13:10, schrieb Peter Alexander:On 5/03/11 4:39 AM, Jonathan M Davis wrote:No, the temporary in this case is not an lvalue. It's an adress whose value is an lvalue. The results of operator[] is a reference not a normal value. A reference is an adress which is always implicitly derefenced when used. In D we use opIndexAssign anyways. MafiOn Friday 04 March 2011 20:31:38 Walter Bright wrote:How do you think array assignments in C++ work? a[i] = x; a[i] is just *(a + i), i.e. the evaluation of an expression that yields a temporary, which in this case is an lvalue. Same applies to all other operator[] overloads.uri wrote:Really? I thought that a temporary was pretty much _the_ classic example of an rvalue. If you can assign to temporaries, you can assign to most anything then, other than literals. Why on earth would assigning to temporaries be permitted? That just seems unnecessary and bug-prone. - Jonathan M DavisExplain why (a*b) is lvalue in bearophile's second example.Because the expression evaluates to a temporary, which is an lvalue.This is one of the weird things in D. The language is too complex. It takes years to find out about the corner cases.It's not a weird corner case at all. Temporaries can be used as lvalues (in C++ too).
Mar 05 2011
On 5/03/11 1:39 PM, Mafi wrote:Am 05.03.2011 13:10, schrieb Peter Alexander:(a + i) is an address (type T*) *(a + i) is a lvalue reference (type T&)How do you think array assignments in C++ work? a[i] = x; a[i] is just *(a + i), i.e. the evaluation of an expression that yields a temporary, which in this case is an lvalue. Same applies to all other operator[] overloads.No, the temporary in this case is not an lvalue. It's an adress whose value is an lvalue.The results of operator[] is a reference not a normal value. A reference is an adress which is always implicitly derefenced when used. In D we use opIndexAssign anyways.A reference is not an address. A reference is a synonym for another object. If that other object is an lvalue then the reference is also an lvalue. Sources: http://www.parashift.com/c++-faq-lite/references.html#faq-8.2 "In compiler writer lingo, a reference is an 'lvalue' (something that can appear on the left hand side of an assignment operator)." http://www.artima.com/cppsource/rvalue.html "To better distinguish these two types, we refer to a traditional C++ reference as an lvalue reference." http://msdn.microsoft.com/en-us/library/64sa8b1e.aspx "The operand of the address-of operator can be either a function designator or an l-value that designates an object" (I can use the address-of operator on *(a+i), and it's not a function designator, therefore it is an l-value)
Mar 05 2011
Am 05.03.2011 16:44, schrieb Peter Alexander:On 5/03/11 1:39 PM, Mafi wrote:I know. I meant the temporary itself (ie a + i) is not an lvalue; it lies around in some register which should the coder should not explicitly write to. The dereferencing only changed what to do with the result. There's no computation behind derefencing.Am 05.03.2011 13:10, schrieb Peter Alexander:(a + i) is an address (type T*) *(a + i) is a lvalue reference (type T&)How do you think array assignments in C++ work? a[i] = x; a[i] is just *(a + i), i.e. the evaluation of an expression that yields a temporary, which in this case is an lvalue. Same applies to all other operator[] overloads.No, the temporary in this case is not an lvalue. It's an adress whose value is an lvalue.A reference is nothing else than a pointer which the compiler handles diferent at compile time. Look /++++++ main.d +++++++++/ import std.stdio; //extern(C) to avoid mangling extern(C) void refTest(ref int x); void main() { int a = 5; refTest(a); a = 7; refTest(a); a = 42; refTest(a); writeln("END"); } /+++++++ test.d ++++++++/ import std.stdio; extern(C) void refTest(int* x) { writefln(" x = %s, *x = %s", x, *x); } /+++++++ compile +++++++/ dmd -c test.d dmd main.d test.obj ./main /+++++ output ++++++++/ x = 12FE44, *x = 5 x = 12FE44, *x = 7 x = 12FE44, *x = 42 END Tested with dmd 2.051 on Win7. Look reference = pointer + implicite derefenceThe results of operator[] is a reference not a normal value. A reference is an adress which is always implicitly derefenced when used. In D we use opIndexAssign anyways.A reference is not an address. A reference is a synonym for another object. If that other object is an lvalue then the reference is also an lvalue........
Mar 05 2011
I didn't see this example being mentioned in this thread (although I might have missed this), but would someone explain why (1) the code below doesn't compile, and (2) why it's considered context-free? struct MyStruct { ref MyStruct opMul(MyStruct x) { return this; } } ... OpOverloadAbuse a, b; a * b = b; Thanks!
Mar 06 2011
On Sunday 06 March 2011 22:38:50 %u wrote:I didn't see this example being mentioned in this thread (although I might have missed this), but would someone explain why (1) the code below doesn't compile, and (2) why it's considered context-free? struct MyStruct { ref MyStruct opMul(MyStruct x) { return this; } } ... OpOverloadAbuse a, b; a * b = b;That's essentially the example that's been under discussion - though in this case it's a ref instead of a temporary for the lvalue. Regardless, it's context free because a * b is by definition a variable declaration, not a call to the multiplication operator. If you want it to use the multiplication operator, then use parens: (a * b) = b. It's context free, because it just assumes one of the two and it's _always_ that one, so there's no ambiguity. It is, _by definition_, a variable declaration. Also, opMul is on its way to deprecation. binaryOp should be used for overloading the multiplication operator. - Jonathan M Davis
Mar 06 2011
That's essentially the example that's been under discussion - though in this case it's a ref instead ofa temporary for the lvalue. Regardless, it's context free because a * b is by definition a variable declaration, not a call to the multiplication operator. If you want it to use the multiplication operator, then use parens: (a * b) = b. It's context free, because it just assumes one of the two and it's _always_ that one, so there's no ambiguity. It is, _by definition_, a variable declaration. Oh, I see. So is multiplication being special-cased in the grammar, or is it part of a more general rule in the language?Also, opMul is on its way to deprecation. binaryOp should be used for overloading the multiplicationoperator. Whoa! I did not know that; thanks.
Mar 06 2011
On Sunday 06 March 2011 23:16:33 %u wrote:It's not really that multiplication is being special-cased. It's that when something could either be a pointer declaration or a multiplicative expression, it's deemed to be a pointer declaration. Anywhere where it wouldn't be a pointer declaration, it's a multiplicative expression.That's essentially the example that's been under discussion - though in this case it's a ref instead ofa temporary for the lvalue. Regardless, it's context free because a * b is by definition a variable declaration, not a call to the multiplication operator. If you want it to use the multiplication operator, then use parens: (a * b) = b. It's context free, because it just assumes one of the two and it's _always_ that one, so there's no ambiguity. It is, _by definition_, a variable declaration. Oh, I see. So is multiplication being special-cased in the grammar, or is it part of a more general rule in the language?_Most_ of the old opX functions are going to be deprecated in favor of functions like opUnary and opBinary - which are far more flexible. You should probably read http://www.digitalmars.com/d/2.0/operatoroverloading.html - and if you can, reading TDPL (The D Programming Language by Andrei Alexandrescu) would be even better. - Jonathan M DavisAlso, opMul is on its way to deprecation. binaryOp should be used for overloading the multiplication
Mar 06 2011
On Mar 7, 11 15:16, %u wrote:Only statement of the form 'p*q=r' is "special-cased". Actually it's not quite fair to say it is "special-cased", just because declarations (which accepts 'p*q=r') are processed before expressions. import std.stdio; struct Foo { ref Foo opBinary(string x) (int a) pure nothrow { return this; } ref Foo opBinaryRight(string x) (int a) pure nothrow { return this; } } void main() { Foo a; int b; a + 1 = a; a * 1 = a; 1 + a = a; 1 * a = a; a + b = a; //a * b = a; }That's essentially the example that's been under discussion - though in this case it's a ref instead ofa temporary for the lvalue. Regardless, it's context free because a * b is by definition a variable declaration, not a call to the multiplication operator. If you want it to use the multiplication operator, then use parens: (a * b) = b. It's context free, because it just assumes one of the two and it's _always_ that one, so there's no ambiguity. It is, _by definition_, a variable declaration. Oh, I see. So is multiplication being special-cased in the grammar, or is it part of a more general rule in the language?Also, opMul is on its way to deprecation. binaryOp should be used for overloading the multiplicationoperator. Whoa! I did not know that; thanks.
Mar 06 2011
"uri" <fan languages.org> wrote in message news:iks9jb$127g$1 digitalmars.com...Jonathan M Davis Wrote:Corner cases are certainly a PITA in certain corner cases. But simplistic languages are a PITA in most everyday cases. If I felt that simpler languages were better I'd use Brainfuck as my primary language.On Friday 04 March 2011 17:05:57 Simon Buerger wrote:Explain why (a*b) is lvalue in bearophile's second example. This is one of the weird things in D. The language is too complex. It takes years to find out about the corner cases. I wouldn't use it for anything reliableIt is often said that D's grammar is easier to parse than C++, i.e. it should be possible to seperate syntactic and semantic analysis, which is not possible in C++ with the template-"< >" and so on. But I found following example: The Line "a * b = c;" can be interpreted in two ways: -> Declaration of variable b of type a* -> (a*b) is itself a lvalue which is assigned to. Current D (gdc 2.051) interprets it always in the first way and yields an error if the second is meant. The Workaround is simply to use parens like "(a*b)=c", so it's not a real issue. But at the same time, C++ (gcc 4.5) has no problem to distinguish it even without parens. So, is the advertising as "context-free grammar" wrong?Umm. How could a * b be assigned to? It's definitely not an lvalue. Do you mean that an overloaded opBinary!"*" is used which returns a ref? It certainly can't be done normally.
Mar 04 2011
The ambiguities are simply resolved by this rule in the language specification: "Any ambiguities in the grammar between Statements and Declarations are resolved by the declarations taking precedence." ( http://www.digitalmars.com/d/2.0/statement.html ). Simon Buerger wrote:It is often said that D's grammar is easier to parse than C++, i.e. it should be possible to seperate syntactic and semantic analysis, which is not possible in C++ with the template-"< >" and so on. But I found following example: The Line "a * b = c;" can be interpreted in two ways: -> Declaration of variable b of type a* -> (a*b) is itself a lvalue which is assigned to. Current D (gdc 2.051) interprets it always in the first way and yields an error if the second is meant. The Workaround is simply to use parens like "(a*b)=c", so it's not a real issue. But at the same time, C++ (gcc 4.5) has no problem to distinguish it even without parens. So, is the advertising as "context-free grammar" wrong? - Krox
Mar 04 2011