4. Basic Grammar Elements
This section defines grammar elements that are used repeatedly in later sections.
4.1 Operator Names
Several places in the grammar refer to an ident-or-op
rather than an ident
:
ident-or-op :=
| ident
| ( op-name )
| (*)
op-name :=
| symbolic-op
| range-op-name
| active-pattern-op-name
range-op-name :=
| ..
| .. ..
active-pattern-op-name :=
| | ident | ... | ident |
| | ident | ... | ident | _ |
In operator definitions, the operator name is placed in parentheses. For example:
let (+++) x y = (x, y)
This example defines the binary operator +++
. The text (+++)
is an ident-or-op
that acts as an
identifier with associated text +++
. Likewise, for active pattern definitions (§ 7), the active pattern
case names are placed in parentheses, as in the following example:
let (|A|B|C|) x = if x < 0 then A elif x = 0 then B else C
Because an ident-or-op
acts as an identifier, such names can be used in expressions. For example:
List.map ((+) 1) [ 1; 2; 3 ]
The three character token (*)
defines the *
operator:
let (*) x y = (x + y)
To define other operators that begin with *
, whitespace must follow the opening parenthesis;
otherwise (*
is interpreted as the start of a comment:
let ( *+* ) x y = (x + y)
Symbolic operators and some symbolic keywords have a compiled name that is visible in the compiled form of F# programs. The compiled names are shown below.
[] op_Nil
:: op_ColonColon
+ op_Addition
- op_Subtraction
* op_Multiply
/ op_Division
** op_Exponentiation
@ op_Append
^ op_Concatenate
% op_Modulus
&&& op_BitwiseAnd
||| op_BitwiseOr
^^^ op_ExclusiveOr
<<< op_LeftShift
~~~ op_LogicalNot
>>> op_RightShift
~+ op_UnaryPlus
~- op_UnaryNegation
= op_Equality
<> op_Inequality
<= op_LessThanOrEqual
>= op_GreaterThanOrEqual
< op_LessThan
> op_GreaterThan
? op_Dynamic
?<- op_DynamicAssignment
|> op_PipeRight
||> op_PipeRight2
|||> op_PipeRight3
<| op_PipeLeft
<|| op_PipeLeft2
<||| op_PipeLeft3
! op_Dereference
>> op_ComposeRight
<< op_ComposeLeft
<@ @> op_Quotation
<@@ @@> op_QuotationUntyped
~% op_Splice
~%% op_SpliceUntyped
~& op_AddressOf
~&& op_IntegerAddressOf
|| op_BooleanOr
&& op_BooleanAnd
+= op_AdditionAssignment
- = op_SubtractionAssignment
*= op_MultiplyAssignment
/= op_DivisionAssignment
.. op_Range
.. .. op_RangeStep
Compiled names for other symbolic operators are op_N1
... Nn
where N1
to Nn
are the names for the
characters as shown in the table below. For example, the symbolic identifier <*
has the compiled
name op_LessMultiply
:
> Greater
< Less
+ Plus
- Minus
* Multiply
= Equals
~ Twiddle
% Percent
. Dot
& Amp
| Bar
@ At
# Hash
^ Hat
! Bang
? Qmark
/ Divide
. Dot
: Colon
( LParen
, Comma
) RParen
[ LBrack
] RBrack
4.2 Long Identifiers
Long identifiers long-ident
are sequences of identifiers that are separated by ‘.
’ and optional
whitespace. Long identifiers long-ident-or-op
are long identifiers that may terminate with an
operator name.
long-ident := ident '.' ... '.' ident
long-ident-or-op :=
| long-ident '.' ident-or-op
| ident-or-op
4.3 Constants
The constants in the following table may be used in patterns and expressions. The individual lexical formats for the different constants are defined in §3.
const :=
| sbyte
| int16
| int32
| int64 -- 8, 16, 32 and 64-bit signed integers
| byte
| uint16
| uint32
| int -- 32 - bit signed integer
| uint64 -- 8, 16, 32 and 64-bit unsigned integers
| ieee32 -- 32 - bit number of type "float32"
| ieee64 -- 64 - bit number of type "float"
| bignum -- User or library-defined integral literal type
| char -- Unicode character of type "char"
| string v -- String of type "string" (System.String)
| verbatim-string -- String of type "string" (System.String)
| triple-quoted-string -- String of type "string" (System.String)
| bytestring -- String of type "byte[]"
| verbatim-bytearray -- String of type "byte[]"
| bytechar -- Char of type "byte"
| false | true -- Boolean constant of type "bool"
| '(' ')' -- unit constant of type "unit"
4.4 Operators and Precedence
4.4.1 Categorization of Symbolic Operators
The following symbolic-op
tokens can be used to form prefix and infix expressions. The marker OP
represents all symbolic-op
tokens that begin with the indicated prefix, except for tokens that appear
elsewhere in the table.
infix-or-prefix-op :=
+, -, +., -., %, &, &&
prefix-op :=
infix-or-prefix-op
~ ~~ ~~~ (and any repetitions of ~)
!OP (except !=)
infix-op :=
infix-or-prefix-op
- OP +OP || <OP >OP = |OP &OP ^OP *OP /OP %OP !=
(or any of these preceded by one or more ‘.’)
:=
::
$
or
?
The operators +
, -
, +.
, -.
, %
, %%
, &
, &&
can be used as both prefix and infix operators. When these
operators are used as prefix operators, the tilde character is prepended internally to generate the
operator name so that the parser can distinguish such usage from an infix use of the operator. For
example, -x
is parsed as an application of the operator ~-
to the identifier x
. This generated name is
also used in definitions for these prefix operators. Consequently, the definitions of the following
prefix operators include the ~
character:
// To completely redefine the prefix + operator:
let (~+) x = x
// To completely redefine the infix + operator to be addition modulo- 7
let (+) a b = (a + b) % 7
// To define the operator on a type:
type C(n:int) =
let n = n % 7
member x.N = n
static member (~+) (x:C) = x
static member (~-) (x:C) = C(-n)
static member (+) (x1:C,x2:C) = C(x1.N+x2.N)
static member (-) (x1:C,x2:C) = C(x1.N-x2.N)
The ::
operator is special. It represents the union case for the addition of an element to the head of
an immutable linked list, and cannot be redefined, although it may be used to form infix expressions.
It always accepts arguments in tupled form — as do all union cases — rather than in curried form.
4.4.2 Precedence of Symbolic Operators and Pattern/Expression Constructs
Rules of precedence control the order of evaluation for ambiguous expression and pattern constructs. Higher precedence items are evaluated before lower precedence items.
The following table shows the order of precedence, from highest to lowest, and indicates whether
the operator or expression is associated with the token to its left or right. The OP
marker represents
the symbolic-op
tokens that begin with the specified prefix, except those listed elsewhere in the
table. For example, +OP
represents any token that begins with a plus sign, unless the token appears
elsewhere in the table.
Operator or expression | Associativity | Comments |
---|---|---|
f |
Left | High-precedence type application; see §15.3 |
f(x) | Left | High-precedence application; see §15.2 |
. | Left | |
prefix-op | Left | Applies to prefix uses of these symbols |
" | rule" | Right Pattern matching rules |
"f x" "lazy x" "assert x" |
Left | |
**OP | Right | |
*OP /OP %OP | Left | |
- OP +OP | Left | Applies to infix uses of these symbols |
:? | Not associative | |
:: | Right | |
^OP | Right | |
!=OP \<OP >OP = |OP &OP $ | Left | |
:> :?> | Right | |
& && | Left | |
or || | Left | |
, | Not associative | |
:= | Right | |
-> | Right | |
if | Not associative | |
function, fun, match, try | Not associative | |
let | Not associative | |
; | Right | |
| | Left | |
when | Right | |
as | Right |
If ambiguous grammar rules (such as the rules from §6) involve tokens in the table, a construct that appears earlier in the table has higher precedence than a construct that appears later in the table.
The associativity indicates whether the operator or construct applies to the item to the left or the right of the operator.
For example, consider the following token stream:
a + b * c
In this expression, the expr infix-op expr
rule for b * c
takes precedence over the
expr infix-op expr
rule for a + b
, because the *
operator has higher precedence than the +
operator. Thus, this
expression can be pictured as follows:
a + b * c
// _________
// _____
rather than
a + b * c
// _________
// _____
Likewise, given the tokens
a * b * c
the left associativity of *
means we can picture the resolution of the ambiguity as:
a * b * c
// _____
In the preceding table, leading .
characters are ignored when determining precedence for infix
operators. For example, .*
has the same precedence as *.
This rule ensures that operators such as
.*
, which is frequently used for pointwise-operation on matrices, have the expected precedence.
The table entries marked as “High-precedence application” and “High-precedence type application” are the result of the augmentation of the lexical token stream, as described in §15.1.2 and §15.3.