qwertie / ecsharp

Home of LoycCore, the LES language of Loyc trees, the Enhanced C# parser, the LeMP macro preprocessor, and the LLLPG parser generator.
http://ecsharp.net
Other
172 stars 25 forks source link

LES3: Attributes on operator targets (and subexpressions)? #91

Open qwertie opened 5 years ago

qwertie commented 5 years ago

I've been looking for a good way to represent properties in LES. There are some straightforward choices that can be used already:

    .prop X: int {
        .get { .return _x }
        .set { _x = value }
    }
    .prop X: int {
        get => x
        set => _x = value
    }
    .prop X: int => x

But these styles (except the last) require a keyword to introduce them (.prop). I guess this wouldn't be too burdensome since you might want a keyword to indicate accessibility anyway...

    .public X: int {
        get => x
        set => _x = value
    }

But fields and functions can be supported without a keyword, which creates an asymmetry:

    foo() => .print "Food" // OK
    _y: int // OK
    Y: int { get => _y } // syntax error

So I was thinking about whether there is a way around this, and I thought of a nicely compact syntax:

    X: int  get _x
    Y: int  get _y  set _y = value
    // Assumes line continuators exist (#86)
    Z: int
    |  get { _y }
    |  set { _y = value; PropertyChanged("Z") }

(maybe too compact? well, I'm fine with it)

It would work for events too (though I'm inclined to insist on a keyword for that case):

    .event PropertyChanged(name: symbol)
    |   add { /* TODO */ }
    |   remove { /* TODO */ }

Then I noticed a problem - since get and set are ordinary binary operators, there is no obvious way to associate attributes with them. So how do we represent the classic case of a public getter with private setter (or the more taboo sealed getter and virtual setter)?

Well... we could technically allow attributes that annotate the operators themselves...

    // proposed syntax
    Z: int
    |  @public get { _y }
    |  @private set { _y = value; }

In the syntax tree, the attributes would annotate the target (not the expression) as follows:

(@public `'get`)( Z: int, (@private `'set`)({_y}, {_y = value}) )

You could, of course, annotate any operator this way:

x @hello + y
// syntax tree: (@hello `'+`)(x, y)

Currently, attributes are only allowed at the beginning of an expression. If operators can have attributes, then surely subexpressions ought to be allowed to have attributes too?

@J energy := 0.5 * @kg mass * @(m**2/s**2) velocity**2

But then the question arises of how to decide how much of an expression the attribute would apply to. Perhaps an attribute can simply be associated with as much of an expression to its right as is logically possible according to the current precedence floor; in that case I believe the expression above would have the following structure.

@J ( energy := 0.5 * (@kg mass) * (@(m**2/s**2) (velocity**2)) )

I'm not entirely sold on either of these proposals, but it certainly seems worth exploring.

qwertie commented 5 years ago

A couple of other things to note about properties...

qwertie commented 5 years ago

The other day I was thinking it would be nice to have a language that supports shorthand operators for public/export, private, virtual etc...

Foo() => {} // default accessibility (package?)
++Foo() => {} // public
+Foo() => {} // internal
*Foo() => {} // protected
+*Foo() => {} // protected | internal
-*Foo() => {} // protected & internal (private protected in C#)
-Foo() => {} // private
^Foo() => {} // virtual
^^Foo() => {} // override
^^^Foo() => {} // sealed override
%Foo() => () // umm... let's say static/singleton

But this would be a lot to remember and wouldn't work on get and set operators. Something that could work as a shorthand is a way of applying modifiers to multiple members:

@public @virtual @CustomAttribute {
    Func1() => { }
    Func2() => { }
    Foo: int  get _foo  @private set _foo = value
}
qwertie commented 5 years ago

I dunno, any thoughts on this @jonathanvdc? One could also argue that we ought to have keywords on all members anyhow, to aid grepping - I'd particularly like a keyword on member variables which I think tend to be more important to keep track of, and find, than functions (even though langs are more likely to have keyword for the latter). In any case I'd like LES to offer as much flexibility as practical to DSL/compiler authors.