Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow --prefix, --infix and --postfix pragmas to refer to later constant declarations #270

Merged
merged 13 commits into from
Sep 11, 2023

Conversation

MartyO256
Copy link
Member

Motivation

Before v1.1, it was possible to declare an identifier as an operator before any constant declaration was attached to that identifier.

For instance, the following snippet first defines ~ as an infix operator, then declares an LF type-level constant with identifier ~. This made it possible to use that infix notation in the definition of LF term-level constants.

--infix ~ 1 right.

LF ~ : tm → exp → type =
  | tr_unit : unit ~ unit'
  | tr_pair : M ~ E → N ~ F
              → (pair M N) ~ (pair' E F)

The re-write of the parser in #266 to support shadowing of constants and the definition of namespaces broke that feature. Indeed, it became necessary for fixity pragmas to refer to constants that are already bound. This meant that parsing the pragma --infix ~ 1 right. would result in an "Unbound identifier ~" exception to be thrown.

The reason why this breaking change occurred is because in the disambiguation state, the notation for an identifier has to be attached to the constant it is meant to affect. This is necessary because opening a module with the --open pragma has to bring that notation back in scope.

Changes

This PR re-introduces the feature of having a --prefix, --infix or --postfix pragma before the declaration it is meant to affect. During the disambiguation of Beluga signatures, when a fixity pragma is encountered, a lookahead is performed to determine if the closest declaration that follows the pragma introduces a constant with the same identifier as the target of the pragma. If it is the case that this next declaration introduces the pragma's target, then the fixity pragma is said to be postponed, in the sense that it is applied once that constant is added to the disambiguation state.

Put more precisely, if P is a fixity pragma with target identifier c, and D is a declaration that introduces a constant with identifier c, then P is a postponed fixity pragma if we have the following in a Beluga signature file:

P (<prefix-pragma> | <infix-pragma> | <postfix-pragma> | <documentation-comment>)* D

This makes it possible to write snippets like the following:

--assoc right.

--infix app 1 none.
--prefix lam 1.
--prefix red 2.

LF term : type =
| app: term -> term -> term =
| lam: (term -> term) -> term
| red: term -> term;

The ./t/code/success/infix/postponed.bel test case is added to showcase this feature.

If a fixity pragma is found not to be postponed, then it affects a constant declared earlier in the signature, like it did in v1.1. The proposed lookahead solution does not introduce ambiguity in the placement of fixity pragmas. If the pragma is between two declarations that use the same identifier, then the pragma is postponed. Attaching the pragma to the earlier declaration would not be useful since that constant is immediately shadowed by the other declaration.

This change affects all the areas where fixity pragmas are involved, namely:

  • Signature disambiguation
  • Pretty-printing of the external syntax
  • Pretty-printing of the external syntax to HTML pages
  • Pretty-printing of the internal syntax (using the legacy OpPragmas store, which is constructed during signature reconstruction)

This ensures that we can print pragmas without applying their effect.
This is useful for postponed fixity pragmas.
…otation`

The verb `add` is more indicative of a side effect on the pretty-printing state.
This test case is derived from `./t/code/success/infix/alternate.bel`.
The difference is that the new style of LF declarations is used, along with infix pragmas that precedence the type family declaration.
Those pragmas are postponed, in the sense that they apply to constants declared after it as opposed to constants already in scope.
This removes some of the duplicated code for determining whether a fixity pragma is postponed or not.
@MartyO256 MartyO256 merged commit 54181ec into master Sep 11, 2023
6 checks passed
@MartyO256 MartyO256 deleted the fixity-lookahead branch September 11, 2023 23:59
MartyO256 added a commit that referenced this pull request Oct 24, 2023
PR #270 did not include the necessary changes to support postponed fixity pragmas in Harpoon sessions.
When a reconstructed Beluga signature is revisited to create snapshots of the disambiguation state, Harpoon would raise an unbound identifier exception for postponed fixity pragmas.
MartyO256 added a commit that referenced this pull request May 7, 2024
This updates the disambiguation algorithm for module entries to support postponed fixity pragmas (#270).
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.

1 participant