Skip to content

Conversation

@LPTK
Copy link
Contributor

@LPTK LPTK commented Jun 10, 2025

new is a strange keyword: it has a very high precedence that sits between that of selection and that of application. Indeed, new Foo().bar should parse as (new Foo()).bar, not new (Foo().bar), but new Foo.Bar should parse as new (Foo.Bar).

I just wanted to fix the precedence of new, but this lead to a formidable yak shave. Notably ended up having to make with parse as an infix keyword (because of the new ... with ... syntax and the incomparable precedences) and having to temporarily refactor the constructor desugaring hack (which will be changed again by #309).

@LPTK LPTK requested a review from FlandiaYingman June 10, 2025 08:09
@FlandiaYingman
Copy link

new Foo().bar should parse as (new Foo()).bar

wow, i thought in other classic languages like C++ and Java they require explicit parentheses here

@LPTK
Copy link
Contributor Author

LPTK commented Jun 10, 2025

Wow are you new to programming? 😛

Copy link

@FlandiaYingman FlandiaYingman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow amazing. I just have some questions about the new new parsing strategy


def isModuleModifier: Bool = this match
case Tree.TypeDef(Mod, _, N, N) => true
case td @ Tree.TypeDef(Mod, _, rhs) => rhs.isEmpty && td.extension.isEmpty && td.withPart.isEmpty

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not

Suggested change
case td @ Tree.TypeDef(Mod, _, rhs) => rhs.isEmpty && td.extension.isEmpty && td.withPart.isEmpty
case td @ Tree.TypeDef(Mod, _, N) => td.extension.isEmpty && td.withPart.isEmpty

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wow

Comment on lines 160 to +169
//│ ═══[WARNING] Modules are not yet lifted.
//│ ╔══[COMPILATION ERROR] No definition found in scope for 'M'
//│ ║ l.154: module M with
//│ ╙── ^
//│ ║ ^^^^^^
//│ ║ l.155: fun foo() =
//│ ║ ^^^^^^^^^^^^^^^
//│ ║ l.156: set y = 2
//│ ║ ^^^^^^^^^^^^^^^
//│ ║ l.157: x + y
//│ ╙── ^^^^^^^^^^^

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think the location is too... long? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, in other prototypes we had logic to abbreviate long locations. We should port that over at some point.

Comment on lines +215 to +216
// val td = DummyTypeDef(syntax.Cls)
val td = TypeDef(syntax.Cls, App(id, Tup(Ident("captures") :: Nil)), N)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// val td = DummyTypeDef(syntax.Cls)
val td = TypeDef(syntax.Cls, App(id, Tup(Ident("captures") :: Nil)), N)
val td = TypeDef(syntax.Cls, App(id, Tup(Ident("captures") :: Nil)), N)

Comment on lines +589 to +592
// case New(c, rfto) =>
// assert(rfto.isEmpty)
// Term.New(cls(subterm(c), inAppPrefix = inAppPrefix), params.map(subterm(_)), bodo).withLocOf(tree)
case ProperNew(body, rfto) => // TODO handle Under

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// case New(c, rfto) =>
// assert(rfto.isEmpty)
// Term.New(cls(subterm(c), inAppPrefix = inAppPrefix), params.map(subterm(_)), bodo).withLocOf(tree)
case ProperNew(body, rfto) => // TODO handle Under
case ProperNew(body, rfto) => // TODO handle Under

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I think this is good now:

Suggested change
// case New(c, rfto) =>
// assert(rfto.isEmpty)
// Term.New(cls(subterm(c), inAppPrefix = inAppPrefix), params.map(subterm(_)), bodo).withLocOf(tree)
case ProperNew(body, rfto) => // TODO handle Under
case ProperNew(body, rfto) =>

Comment on lines +138 to +144
// * `new` is a strange keyword:
// * it has a very high precedence that sits between that of selection and that of application.
// * Indeed, `new Foo().bar` should parse as `(new Foo()).bar`, not `new (Foo().bar)`,
// * but `new Foo.Bar` should parse as `new (Foo.Bar)`.
val newRightPrec = S(maxPrec.get + charPrecList.length - 1)
// * ^ maxPrec.get + charPrecList.length is the precedence of selection
val `new` = Keyword("new", N, newRightPrec)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, how does this mysterious precedence definition make it possible to parse new before application but after selection 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic could be improved for clarity, for sure.

Chec out how charPrecList reserves a slot for new that sits between the slot for "." and that for applications.

@LPTK LPTK merged commit ba89d62 into hkust-taco:hkmc2 Jun 11, 2025
1 check passed
@LPTK LPTK deleted the hkmc2-parsing-new-with branch June 11, 2025 00:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants