Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions spec/lexical-analysis.md
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ The following symbolic or partially symbolic character sequences are treated as
```fsgrammar
token symbolic-keyword =
let! use! do! yield! return!
| -> <-. : ( ) [ ] [< >] [| |] { }
| -> <- . : ( ) [ ] [< >] [| |] { }
' # :?> :? :> .. :: := ;; ; =
_? ?? (*) <@ @> <@@ @@>
```
Expand All @@ -321,7 +321,7 @@ except where the sequence of characters is a symbolic keyword ([§](lexical-anal

```fsgrammar
regexp first-op-char = !%&*+-./<=>@^|~
regexp op-char = first-op-char |?
regexp op-char = first-op-char | [?:]

token quote-op-left =
| <@ <@@
Expand All @@ -330,7 +330,7 @@ token quote-op-right =
| @> @@>

token symbolic-op =
|?
| ?
| ?<-
| first-op-char op-char *
| quote-op-left
Expand All @@ -340,6 +340,9 @@ token symbolic-op =
For example, `&&&` and `|||` are valid symbolic operators. Only the operators `?` and `?<-` may start with
`?`.

Use of `:` in symbolic operators is partially reserved. It may only be used in operators where the first character
is `>` or where the first character after any number of leading `.` is `>` e.g. `>:` or `.>:`.
Copy link
Collaborator

@Martin521 Martin521 Sep 28, 2025

Choose a reason for hiding this comment

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

This is a weird situation. Given this comment I would have expected : to become a regular op-char. I think this deserves a separate language suggestion to rectify it.

But now that it exists we have to deal with it in the spec. I don't think this comment is the best way to do it. The grammar should be the source of truth (and there are no "reserved symbolic operators"). So, I see two options.
a) Adapt the grammar to describe the current situation (we might need to introduce a last-op-char term).
b) Leave the grammar as is, remove the comment and assume the current behavior is a compiler bug.
I am tending toward b), but am interested in other opinions. Maybe I am overlooking something.

Copy link
Contributor Author

@roboz0r roboz0r Sep 28, 2025

Choose a reason for hiding this comment

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

I'm not quite sure how to read .fsl syntax but these seem to be be relevant sections here

https://github.com/roboz0r/fsharp/blob/8d12fd301c5a52649b09f2e31dba2ae0cf757419/src/Compiler/lex.fsl#L238-L240
https://github.com/roboz0r/fsharp/blob/8d12fd301c5a52649b09f2e31dba2ae0cf757419/src/Compiler/lex.fsl#L110-L118
https://github.com/roboz0r/fsharp/blob/8d12fd301c5a52649b09f2e31dba2ae0cf757419/src/Compiler/lex.fsl#L988-L1004

Based on my reading :, $ and ? are always lexed to be part of operators but are then tagged with errors.

This construct is deprecated: '$' is not permitted as a character in operator names and is reserved for future use

The other note in the spec about ? seems to just be incorrect too.

Only the operators ? and ?<- may start with ?.

This code compiles fine:

let (?+) a b = if a then b + 1 else b
true ?+ 7

Trying to come up with some declarative rules for this is a bit tricky but I think this works if token symbolic-op is always attempted before token reserved-symbolic-op.

regexp op-char = !%&*+-./<=>?@^|~
regexp ignored-op-char = .?
regexp op-colon = :
regexp op-greater = >
regexp op-dollar = $

token quote-op-left =
    | <@ <@@

token quote-op-right =
    | @> @@>

token symbolic-op =
    | op-char +
    | ignored-op-char * op-greater (op-colon | op-char) *
    | quote-op-left
    | quote-op-right

token reserved-symbolic-op =
    | (op-char | op-colon | op-dollar) +

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Seems like ? has been allowed in arbitrary operators at least as far back as 11 years https://github.com/dotnet/fsharp/blame/9e2f161b036cf3d91998d86f966eac532bea1659/src/fsharp/FSharp.Core/Linq.fs

Choose a reason for hiding this comment

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

Seems like ? has been allowed in arbitrary operators at least as far back as 11 years https://github.com/dotnet/fsharp/blame/9e2f161b036cf3d91998d86f966eac532bea1659/src/fsharp/FSharp.Core/Linq.fs

Yeah, the documentation has (always?) been incomplete about this. I meant to try to update it back when I was doing the parentheses analysis, but I never got around to it...

Zero or more leading dots or question marks can be added to any infix operator and have no effect on precedence or associativity with the exception of &, &&, and || (whose bare forms are treated as special cases).


The `quote-op-left` and `quote-op-right` operators are used in quoted expressions ([§](expressions.md#quoted-expressions)).

For details about the associativity and precedence of symbolic operators in expression forms, see
Expand Down Expand Up @@ -388,7 +391,7 @@ token unativeint = ( int | xint ) 'un' For example, 34un
token int64 = ( int | xint ) 'L' For example, 34L
token uint64 = ( int | xint ) 'UL' For example, 34UL
| ( int | xint ) 'uL' For example, 34uL

token float =
int . int?
int (. int?)? (e|E) (+|-)? int
Expand Down