# Lexical structure

Every EdgeQL command is composed of a sequence of *tokens*, terminated by a semicolon (`;`). The types of valid tokens as well as their order is determined by the syntax of the particular command.

EdgeQL is case sensistive except for *keywords* (in the examples the keywords are written in upper case as a matter of convention).

There are several kinds of tokens: *keywords*, *identifiers*, *literals* (constants) and *symbols* (operators and punctuation).

Tokens are normally separated by whitespace (space, tab, newline) or comments.

## Identifiers

There are two ways of writing identifiers in EdgeQL: plain and quoted. The plain identifiers are similar to many other languages, they are alphanumeric with underscores and cannot start with a digit. The quoted identifiers start and end with a *backtick* ``quoted.identifier`` and can contain any characters inside with a few exceptions. They must not start with an ampersand (`@`) or contain a double colon (`::`). If there's a need to include a backtick character as part of the identifier name a double-backtick sequence (````) should be used: ``quoted``identifier`` will result in the actual identifier being `quoted`identifier`.

| identifier | ::= | [`plain_ident`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-plain_ident) | [`quoted_ident`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-quoted_ident) |
| plain_ident | ::= | [`ident_first`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-ident_first)[`ident_rest`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-ident_rest)* |
| ident_first | ::= |  <any letter, underscore> |
| ident_rest | ::= |  <any letter, digits, underscore> |
| quoted_ident | ::= |  "`" [`qident_first`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-qident_first)[`qident_rest`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-qident_rest)* "`" |
| qident_first | ::= |  <any character except "@"> |
| qident_rest | ::= |  <any character> |

Quoted identifiers are usually needed to represent module names that contain a dot (`.`) or to distinguish *names* from *reserved keywords* (for instance to allow referring to a link named "order" as ``order``).

## Names and keywords

There are a number of *reserved* and *unreserved* keywords in EdgeQL. Every identifier that is not a *reserved* keyword is a valid *name*. *Names* are used to refer to concepts, links, link properties, etc.

| short_name | ::= | [`not_keyword_ident`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-not_keyword_ident) | [`quoted_ident`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-quoted_ident) |
| not_keyword_ident | ::= |  <any [`plain_ident`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-plain_ident) except for [`keyword`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-keyword)> |
| keyword | ::= | [`reserved_keyword`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-reserved_keyword) | [`unreserved_keyword`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-unreserved_keyword) |
| reserved_keyword | ::= |  case insensitive sequence matching any |
|  | ::= |  of the following |
|  | ::= |  "AGGREGATE" | "ALTER" | "AND" | |
|  | ::= |  "ANY" | "COMMIT" | "CREATE" | |
|  | ::= |  "DELETE" | "DETACHED" | "DISTINCT" | |
|  | ::= |  "DROP" | "ELSE" | "EMPTY" | "EXISTS" | |
|  | ::= |  "FALSE" | "FILTER" | "FUNCTION" | |
|  | ::= |  "GET" | "GROUP" | "IF" | "ILIKE" | |
|  | ::= |  "IN" | "INSERT" | "IS" | "LIKE" | |
|  | ::= |  "LIMIT" | "MODULE" | "NOT" | "OFFSET" | |
|  | ::= |  "OR" | "ORDER" | "OVER" | |
|  | ::= |  "PARTITION" | "ROLLBACK" | "SELECT" | |
|  | ::= |  "SET" | "SINGLETON" | "START" | "TRUE" | |
|  | ::= |  "UPDATE" | "UNION" | "WITH" |
| unreserved_keyword | ::= |  case insensitive sequence matching any |
|  | ::= |  of the following |
|  | ::= |  "ABSTRACT" | "ACTION" | "AFTER" | |
|  | ::= |  "ARRAY" | "AS" | "ASC" | "ATOM" | |
|  | ::= |  "ANNOTATION" | "BEFORE" | "BY" | |
|  | ::= |  "CONCEPT" | "CONSTRAINT" | |
|  | ::= |  "DATABASE" | "DESC" | "EVENT" | |
|  | ::= |  "EXTENDING" | "FINAL" | "FIRST" | |
|  | ::= |  "FOR" | "FROM" | "INDEX" | |
|  | ::= |  "INITIAL" | "LAST" | "LINK" | |
|  | ::= |  "MAP" | "MIGRATION" | "OF" | "ON" | |
|  | ::= |  "POLICY" | "PROPERTY" | |
|  | ::= |  "REQUIRED" | "RENAME" | "TARGET" | |
|  | ::= |  "THEN" | "TO" | "TRANSACTION" | |
|  | ::= |  "TUPLE" | "VALUE" | "VIEW" |

Fully-qualified names consist of a module, `::`, and a short name. They can be used in most places where a short name can appear (such as paths and shapes).

| name | ::= | [`short_name`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-short_name) | [`fq_name`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-fq_name) |
| fq_name | ::= | [`short_name`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-short_name) "::" [`short_name`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-short_name) | |
|  | ::= | [`short_name`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-short_name) "::" [`unreserved_keyword`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-unreserved_keyword) |

## Constants

A number of scalar types have literal constant expressions.

### Strings

Production rules for [`str`](https://docs.geldata.com/reference/stdlib/string.md#type::std::str) literals:

| string | ::= | [`str`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-str) | [`raw_str`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-raw_str) |
| str | ::= |  "'" [`str_content`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-str_content)* "'" | '"' [`str_content`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-str_content)* '"' |
| raw_str | ::= |  "r'" [`raw_content`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-raw_content)* "'" | |
|  | ::= |  'r"' [`raw_content`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-raw_content)* '"' | |
|  | ::= | [`dollar_quote`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-dollar_quote)[`raw_content`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-raw_content)* [`dollar_quote`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-dollar_quote) |
| raw_content | ::= |  <any character different from delimiting quote> |
| dollar_quote | ::= |  "$" [`q_char0`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-q_char0)? [`q_char`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-q_char)* "$" |
| q_char0 | ::= |  "A"..."Z" | "a"..."z" | "_" |
| q_char | ::= |  "A"..."Z" | "a"..."z" | "_" | "0"..."9" |
| str_content | ::= |  <newline> | [`unicode`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-unicode) | [`str_escapes`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-str_escapes) |
| unicode | ::= |  <any printable unicode character not preceded by "\"> |
| str_escapes | ::= |  <see below for details> |

The inclusion of "high ASCII" character in [`edgeql:q_char`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-q_char) in practice reflects the ability to use some of the letters with diacritics like `ò` or `ü` in the dollar-quote delimiter.

Here's a list of valid [`edgeql:str_escapes`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-str_escapes):

| Escape Sequence | Meaning                                                                     |
| --------------- | --------------------------------------------------------------------------- |
| `\[newline]`    | Backslash and all whitespace up to next non-whitespace character is ignored |
| `\\`            | Backslash (\)                                                               |
| `\'`            | Single quote (')                                                            |
| `\"`            | Double quote (")                                                            |
| `\b`            | ASCII backspace (`\x08`)                                                    |
| `\f`            | ASCII form feed (`\x0C`)                                                    |
| `\n`            | ASCII newline (`\x0A`)                                                      |
| `\r`            | ASCII carriage return (`\x0D`)                                              |
| `\t`            | ASCII tabulation (`\x09`)                                                   |
| `\xhh`          | Character with hex value hh                                                 |
| `\uhhhh`        | Character with 16-bit hex value hhhh                                        |
| `\Uhhhhhhhh`    | Character with 32-bit hex value hhhhhhhh                                    |

Here's some examples of regular strings using escape sequences

```edgeql-repl
db> select 'hello
... world';
{'hello
world'}

db> select "hello\nworld";
{'hello
world'}

db> select 'hello \
...         world';
{'hello world'}

db> select 'https://geldata.com/\
...         docs/edgeql/lexical\
...         #constants';
{'https://geldata.com/docs/edgeql/lexical#constants'}

db> select 'hello \\ world';
{'hello \ world'}

db> select 'hello \'world\'';
{"hello 'world'"}

db> select 'hello \x77orld';
{'hello world'}

db> select 'hello \u0077orld';
{'hello world'}
```

Raw strings don't have any specially interpreted symbols; they contain all the symbols between the quotes exactly as typed.

```edgeql-repl
db> select r'hello \\ world';
{'hello \\ world'}

db> select r'hello \
... world';
{'hello \
 world'}

db> select r'hello
... world';
{'hello
 world'}
```

#### Dollar-quoted String Constants

A special case of raw strings are *dollar-quoted* strings. They allow using either kind of quote symbols `'` or `"` as part of the string content without the quotes terminating the string. In fact, because the *dollar-quote* delimiter sequences can have arbitrary alphanumeric additional fillers, it is always possible to surround any content with *dollar-quotes* in an unambiguous manner:

```edgeql-repl
db> select $$hello
... world$$;
{'hello
world'}

db> select $$hello\nworld$$;
{'hello\nworld'}

db> select $$"hello" 'world'$$;
{"\"hello\" 'world'"}

db> select $a$hello$$world$$$a$;
{'hello$$world$$'}
```

More specifically, a delimiter:

- Must start with an ASCII letter or underscore
- Has following characters that can be digits 0-9, underscores or ASCII letters

### Bytes

Production rules for [`bytes`](https://docs.geldata.com/reference/stdlib/bytes.md#type::std::bytes) literals:

| bytes | ::= |  "b'" [`bytes_content`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-bytes_content)* "'" | 'b"' [`bytes_content`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-bytes_content)* '"' |
| bytes_content | ::= |  <newline> | [`ascii`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-ascii) | [`bytes_escapes`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-bytes_escapes) |
| ascii | ::= |  <any printable ascii character not preceded by "\"> |
| bytes_escapes | ::= |  <see below for details> |

Here's a list of valid [`edgeql:bytes_escapes`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-bytes_escapes):

| Escape Sequence | Meaning                        |
| --------------- | ------------------------------ |
| `\\`            | Backslash (\)                  |
| `\'`            | Single quote (')               |
| `\"`            | Double quote (")               |
| `\b`            | ASCII backspace (`\x08`)       |
| `\f`            | ASCII form feed (`\x0C`)       |
| `\n`            | ASCII newline (`\x0A`)         |
| `\r`            | ASCII carriage return (`\x0D`) |
| `\t`            | ASCII tabulation (`\x09`)      |
| `\xhh`          | Character with hex value hh    |

### Integers

There are two kinds of integer constants: limited size ([`int64`](https://docs.geldata.com/reference/stdlib/numbers.md#type::std::int64)) and unlimited size ([`bigint`](https://docs.geldata.com/reference/stdlib/numbers.md#type::std::bigint)). Unlimited size integer [`bigint`](https://docs.geldata.com/reference/stdlib/numbers.md#type::std::bigint) literals are similar to a regular integer literals with an `n` suffix. The production rules are as follows:

| bigint | ::= | [`integer`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-integer) "n" |
| integer | ::= |  "0" | [`non_zero`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-non_zero)[`digit`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-digit)* |
| non_zero | ::= |  "1"..."9" |
| digit | ::= |  "0"..."9" |

By default all integer literals are interpreted as [`int64`](https://docs.geldata.com/reference/stdlib/numbers.md#type::std::int64), while an explicit cast can be used to convert them to [`int16`](https://docs.geldata.com/reference/stdlib/numbers.md#type::std::int16) or [`int32`](https://docs.geldata.com/reference/stdlib/numbers.md#type::std::int32):

```edgeql-repl
db> select 0;
{0}

db> select 123;
{123}

db> select <int16>456;
{456}

db> select <int32>789;
{789}
```

Examples of [`bigint`](https://docs.geldata.com/reference/stdlib/numbers.md#type::std::bigint) literals:

```edgeql-repl
db> select 123n;
{123n}

db> select 12345678901234567890n;
{12345678901234567890n}
```

### Real Numbers

Just as for integers, there are two kinds of real number constants: limited precision ([`float64`](https://docs.geldata.com/reference/stdlib/numbers.md#type::std::float64)) and unlimited precision ([`decimal`](https://docs.geldata.com/reference/stdlib/numbers.md#type::std::decimal)). The [`decimal`](https://docs.geldata.com/reference/stdlib/numbers.md#type::std::decimal) constants have the same lexical structure as [`float64`](https://docs.geldata.com/reference/stdlib/numbers.md#type::std::float64), but with an `n` suffix:

| decimal | ::= | [`float`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-float) "n" |
| float | ::= | [`float_wo_dec`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-float_wo_dec) | [`float_w_dec`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-float_w_dec) |
| float_wo_dec | ::= | [`integer_part`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-integer_part)[`exp`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-exp) |
| float_w_dec | ::= | [`integer_part`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-integer_part) "." [`decimal_part`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-decimal_part)? [`exp`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-exp)? |
| integer_part | ::= |  "0" | [`non_zero`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-non_zero)[`digit`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-digit)* |
| decimal_part | ::= | [`digit`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-digit)+ |
| exp | ::= |  "e" ("+" | "-")? [`digit`](https://docs.geldata.com/reference/reference/edgeql/lexical.md#grammar-token-edgeql-digit)+ |

By default all float literals are interpreted as [`float64`](https://docs.geldata.com/reference/stdlib/numbers.md#type::std::float64), while an explicit cast can be used to convert them to [`float32`](https://docs.geldata.com/reference/stdlib/numbers.md#type::std::float32):

```edgeql-repl
db> select 0.1;
{0.1}

db> select 12.3;
{12.3}

db> select 1e3;
{1000.0}

db> select 1.2e-3;
{0.0012}

db> select <float32>12.3;
{12.3}
```

Examples of [`decimal`](https://docs.geldata.com/reference/stdlib/numbers.md#type::std::decimal) literals:

```edgeql-repl
db> select 12.3n;
{12.3n}

db> select 12345678901234567890.12345678901234567890n;
{12345678901234567890.12345678901234567890n}

db> select 12345678901234567890.12345678901234567890e-3n;
{12345678901234567.89012345678901234567890n}
```

## Punctuation

EdgeQL uses `;` as a statement separator. It is idempotent, so multiple repetitions of `;` don't have any additional effect.

## Comments

Comments start with a `#` character that is not otherwise part of a string literal and end at the end of line. Semantically, a comment is equivalent to whitespace.

| comment | ::= |  "#" <any other characters until the end of line> |

## Operators

EdgeQL operators listed in order of precedence from lowest to highest:

| operator                                                                                                                                                                                                                                                                                                                                 |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`union`](https://docs.geldata.com/reference/stdlib/set.md#operator::union)                                                                                                                                                                                                                                                              |
| [`if..else`](https://docs.geldata.com/reference/stdlib/set.md#operator::if..else)                                                                                                                                                                                                                                                        |
| [`or`](https://docs.geldata.com/reference/stdlib/bool.md#operator::or)                                                                                                                                                                                                                                                                   |
| [`and`](https://docs.geldata.com/reference/stdlib/bool.md#operator::and)                                                                                                                                                                                                                                                                 |
| [`not`](https://docs.geldata.com/reference/stdlib/bool.md#operator::not)                                                                                                                                                                                                                                                                 |
| [`=`](https://docs.geldata.com/reference/stdlib/generic.md#operator::eq), [`!=`](https://docs.geldata.com/reference/stdlib/generic.md#operator::neq), [`?=`](https://docs.geldata.com/reference/stdlib/generic.md#operator::coaleq), [`?!=`](https://docs.geldata.com/reference/stdlib/generic.md#operator::coalneq)                     |
| [`<`](https://docs.geldata.com/reference/stdlib/generic.md#operator::lt), [`>`](https://docs.geldata.com/reference/stdlib/generic.md#operator::gt), [`<=`](https://docs.geldata.com/reference/stdlib/generic.md#operator::lteq), [`>=`](https://docs.geldata.com/reference/stdlib/generic.md#operator::gteq)                             |
| [`like`](https://docs.geldata.com/reference/stdlib/string.md#operator::like), [`ilike`](https://docs.geldata.com/reference/stdlib/string.md#operator::ilike)                                                                                                                                                                             |
| [`in`](https://docs.geldata.com/reference/stdlib/set.md#operator::in), [`not in`](https://docs.geldata.com/reference/stdlib/set.md#operator::in)                                                                                                                                                                                         |
| [`is`](https://docs.geldata.com/reference/stdlib/type.md#operator::is), [`is not`](https://docs.geldata.com/reference/stdlib/type.md#operator::is)                                                                                                                                                                                       |
| [`+`](https://docs.geldata.com/reference/stdlib/numbers.md#operator::plus), [`-`](https://docs.geldata.com/reference/stdlib/numbers.md#operator::minus), [`++`](https://docs.geldata.com/reference/stdlib/string.md#operator::strplus)                                                                                                   |
| [`*`](https://docs.geldata.com/reference/stdlib/numbers.md#operator::mult), [`/`](https://docs.geldata.com/reference/stdlib/numbers.md#operator::div), [`//`](https://docs.geldata.com/reference/stdlib/numbers.md#operator::floordiv), [`%`](https://docs.geldata.com/reference/stdlib/numbers.md#operator::mod)                        |
| [`??`](https://docs.geldata.com/reference/stdlib/set.md#operator::coalesce)                                                                                                                                                                                                                                                              |
| [`distinct`](https://docs.geldata.com/reference/stdlib/set.md#operator::distinct), unary [`-`](https://docs.geldata.com/reference/stdlib/numbers.md#operator::uminus)                                                                                                                                                                    |
| [`^`](https://docs.geldata.com/reference/stdlib/numbers.md#operator::pow)                                                                                                                                                                                                                                                                |
| [`type cast`](https://docs.geldata.com/reference/stdlib/type.md#operator::cast)                                                                                                                                                                                                                                                          |
| [`array[]`](https://docs.geldata.com/reference/stdlib/array.md#operator::arrayidx), [`str[]`](https://docs.geldata.com/reference/stdlib/string.md#operator::stridx), [`json[]`](https://docs.geldata.com/reference/stdlib/json.md#operator::jsonidx), [`bytes[]`](https://docs.geldata.com/reference/stdlib/bytes.md#operator::bytesidx) |
| [`detached`](https://docs.geldata.com/reference/reference/edgeql/with.md#keyword::detached)                                                                                                                                                                                                                                              |

