Składnia

Analiza leksykalna określa odwzorowanie sekwencji znaków tekstu źródłowego na sekwencję tokenów leksykalnych. Tokeny są następnie analizowane składniowo zgodnie z regułami składniowymi. Jeżeli w danym miejscu tekstu źródłowego kilka reguł leksykalnych opisuje poprawny token, za rozpoznany uznaje się token odpowiadający najdłuższej sekwencji znaków.

Realizację analizy leksykalnej i składniowej opisano w dodatku Gramatyka, w postaci specyfikacji PEG (ang. Parsing Expression Grammar). Opisana dalej składnia będzie odwoływać się do symboli reguł w niej zawartych.

Tekst Źródłowy

Tekst źródłowy to sekwencja znaków zakodowanych w jednym z formatów transformacji UTF, zgodnie z normą Unicode.

Wspierane formaty transformacji i rozpoznawane BOM’y (ang. Byte Order Mark) na początku tekstu źródłowego to:

Format BOM

UTF-8

0xEF 0xBB 0xBF

UTF-16BE

0xFE 0xFF

UTF-16LE

0xFF 0xFE

UTF-32BE

0x00 0x00 0xFE 0xFF

UTF-32LE

0xFF 0xFE 0x00 0x00

Tekst źródłowy, jeśli bez BOM’u, jest interpretowany jako UTF-8, zamiast 7-bitowego ASCII będącego jego podzbiorem.

Znaki Białe

TODO

Komentarze

TODO

Identyfikatory

TODO

Identyfikatory Cytowane

TODO

Indeksy Porządkowe

TODO

Literały

TODO

Literały Łańcuchowe

TODO

Sekwencje Ucieczki

Sekwencja Znaczenie

\p

Nowa linia zależna od platformy: CRLF na Windows, LF na Unix

\r

Powrót Karetki (CR, carriage return)

\n

Nowa Linia (LF, line feed)

\f

Koniec Strony (FF, form feed)

\t

Tabulacja Pozioma (TAB)

\v

Tabulacja Pionowa (VT)

\\

Odwrotny Ukośnik (Backslash)

\"

Cudzysłów (Double Quote)

\'

Apostrof (Single Quote)

\a

Sygnał Dźwiękowy (BEL)

\b

Backspace (BS)

\e

Escape (ESC)

\xNN

Wartość 8-bitowa zapisana w formacie szesnastkowym dwiema cyframi

\u{NNNNNN}

Wartość Unicode zapisana w formacie szesnastkowym jedną lub więcej cyfr

Maksymalna wartość skalarna punktu kodowego Unicode to 0x10ffff.

Wieloliniowe Literały Łańcuchowe

TODO

Literały Punktu Kodowego Unicode

TODO

Literały Liczbowe

TODO

Słowa Wbudowane

Słowo zapisane z prefiksem znaku małpy @ i rozpoczynające się od litery lub podkreślenia, używając dalej reguły leksykalnej identyfikatora, utworzy token BUILTIN. Pod tymi słowami kryją się sygnały wbudowane dostarczane przez kompilator.

Słowa Kluczowe

Poniższe identyfikatory są zarezerwowane przez składnię i tworzą tokeny słów kluczowych:

in inout lov out ro rw wo

Wyrażenia

TODO

Tokeny Operatorów

TODO

Notacja Polska (Prefiksowa)

TODO

Deklaracje

TODO

Wiązania

TODO

Zmienne

TODO

Typy Danych

TODO

Operatory

TODO

Lovage dla wszystkich operatorów używa wyłącznie notacji prefiksowej, z czego nie definiuje się zasad pierwszeństwa (ang. precedence) operatorów, tak jak dla języków, gdzie występują wyrażenia operatorowe infiksowe. Pierwszeństwo ma najbardziej wewnętrzny argument kompletnego wyrażenia.

Żaden z operatorów, szczególnie w przypadku operacji logicznych, nie posiada mechanizmu krótkiego zwarcia, dlatego dla każdego wyrażenia operatorowego prawdziwe jest to, że każdy z jego argumentów zostaje poddanym ewaluacji w czasie wykonania.

Arność Operatora

TODO monady, dyady, triady, polyady.

Reguły obniżania. Monady i dyady jednego operatora to zwykle różne operacje. Triady są albo osobną operacją albo trzy-argumentową specjalizacją operatora, co może ale nie musi implikować szans dla optymalizacji generowanego kodu. Polyady to zawsze forma zapisana w nawiasach (op a b c ...) co sprowadza ją do operacji monady/dyady/triady, albo jeśli zdefiniowano dla operatora, folda do szeregu operacji dyadowych. Niektóre mechanizmy mogą modyfikować arność operatora, szczególnie w przypadku promocji monady do dyady, szczególnie mowa o mechanizmach rekurencji lub sekwencyjnej kompozycji.

TODO

Tabela Operatorów

Operacja Notacja Realizacja Semantyka

Kompozycja Równoległa

(, x y z \...)

@parallel

Kompozycja Sekwencyjna

(... x y z \...)

@sequence

(z (y (x)))

Kompozycja Rozdzielania

(.. x y z \...)

@split

Kompozycja Scalania

(: x y z \...)

@merge

Kompozycja Rekurencji

(\ x y z \...)

@recursive

Projekcja Domeny

(. x y z \...)

@domain

Operatory ^

Operacja Notacja Realizacja Semantyka

Eksponent

^x
(^ x)

@exp

Potęga

^x .y
(^ x y \...)

@pow

Logarytm Naturalny

^.x
(^. x)

@ln

Logarytm

^.x .y
(^. x y \...)

@log

Pierwiastek Kwadratowy

^:x
(^: x)

@sqrt

Pierwiastek

^:x .y
(^: x y \...)
^x .%1 .y

Operatory /

Operacja Notacja Realizacja Semantyka

Wartość Absolutna

/x
(/ x)

@abs

Przesunięcie Bitowe w Lewo

/x .y
(/ x y)

@shl

Interpolacja Liniowa

/x :y z
(/ x y z)

@mix

/.x
(/. x)

Przesunięcie Bitowe Arytmetyczne w Prawo

/.x .y
(/. x y)

@sar

/.x :y z
(/. x y z)

Saturacja

/:x
(/: x)
/:x :0.0 1.0

Ogranicza wartość zmiennoprzecinkową do zakresu [0.0f, 1.0f].
Oblicza równanie (@min (@max x 0.0) 1.0).

Przesunięcie Bitowe Logiczne w Prawo

/:x .y
(/: x y)

@shr

Zacisk (Clamp)

/:x :y z
(/: x y z)

@clamp

Równoważne z zapisem (@min (@max x y) z).

Operatory |

Operacja Notacja Realizacja Semantyka

Długość Wektora

|x
(| x)

@length

Dla wartości skalarnej jest sprowadzane do @abs.

Wektor Odbity

|x .y
(| x y)

@reflect

Wektor Transmitowany

|x :y z
(| x y z)

@refract

Transformata Fouriera

|.x
(|. x)

@fft

Opóźnienie Sygnału

|.x .y
(|. x y)

@delay

Estymator Monte-Carlo

|.x :y z
(|. x y z)

@monte-carlo

Odwrócona Transformata Fouriera

|:x
(|: x)

@ifft

Wyprzedzenie Sygnału

|:x .y
(|: x y)
|.x -y

Fresnel-Schlick

|:x :y z
(|: x y z)

@schlick

Operatory &

Operacja Notacja Realizacja Semantyka
& &. &:

Operatory ~

Operacja Notacja Realizacja Semantyka

Bitowy-NOT

~x
(~ x)

@bit-not

Bitowy-XOR

~x .y
(~ x y \...)

@bit-xor

Liczenie Bitów

~.x
(~. x)

@bit-count

Bitowy-OR

~.x .y
(~. x y \...)

@bit-or

Odwrócenie Kolejności Bitów

~:x
(~: x)

@bit-reverse

Bitowy-AND

~:x .y
(~: x y)

@bit-and

Operatory !

Operacja Notacja Realizacja Semantyka
! !. !:

Operatory ?

Operacja Notacja Realizacja Semantyka

Typ Opcjonalny
Czy Istnieje Wartość

?x
(? x)

@optional

Filtracja

?x .y
(? x y)

@filter

Selekcja

?x :y z
(? x y z)

@select

Operatory $

Operacja Notacja Realizacja Semantyka
$ $. $:

Operatory #

Operacja Notacja Realizacja Semantyka
# #. #:

Operatory %

Operacja Notacja Realizacja Semantyka

Odwrotność

%x
(% x)

@rcp

Sprowadzane do instrukcji maszynowej (jeśli dostępna), inaczej jest równoważne z zapisem (% 1 x).

Dzielenie

%x .y
(% x y \...)

@div

Odwrotność Pierwiastka Kwadratowego

%.x
(%. x)

@rsqrt

Sprowadzane do instrukcji maszynowej (jeśli dostępna), inaczej jest równoważne z zapisem (% 1 (^: x)).

Modulo

%.x .y
(%. x y \...)

@mod

Normalizacja

%:x
(%: x)

@normalize

Reszta z Dzielenia

%:x .y
(%: x y \...)

@rem

Operatory *

Operacja Notacja Realizacja Semantyka

Signum

*x
(* x)

@signum

Mnożenie

*x .y
(* x y \...)

@mul

Wyznacznik Macierzy

*.x
(*. x)

@determinant

Logiczny-AND (LCM)

*.x .y
(*. x y \...)

@lcm

Najmniejsza wspólna wielokrotność.

Potęga Do Kwadratu

*:x
(*: x)
^x .2

Logiczny-NAND

*:x .y
*:x :y z
(*: x y \...)
-.(*. x y \...)

Operatory +

Operacja Notacja Realizacja Semantyka

Sprzężenie

+x
(+ x)

@conjugate

Dodawanie

+x .y
(+ x y \...)

@add

Części Liczby Zespolonej

+.x
(+. x)

@real-imag

Logiczny-OR (GCD)

+.x .y
(+. x y \...)

@gcd

Największy wspólny dzielnik.

Inkrementacja

+:x
(+: x)
+x .1

Logiczny-NOR

+:x .y
+:x :y z
(+: x y \...)
-.(+. x y \...)

Operatory -

Operacja Notacja Realizacja Semantyka

Negacja

-x
(- x)

@neg

Odejmowanie

-x .y
-x :y z
(- x y \...)

@sub

Logiczny-NOT

-.x
(-. x)

@not

Nierówność

-.x .y
(-. x y \...)

@neq

Dekrementacja

-:x
(-: x)
-x .1
-:x .y
-:x :y z
(-: x y \...)

Operatory =

Operacja Notacja Realizacja Semantyka

Punkt Stały Sygnału

=x
(= x)

@fix

Równość

=x .y
(= x y)

@eq

Operatory <

Operacja Notacja Realizacja Semantyka

Liczenie Zer Wiodących

<x
(< x)

@clz

Mniejsze

<x .y
(< x y)

@lt

Podłoga

<.x
(<. x)

@floor

Minimalna

<.x .y
(<. x y)

@min

Część Ułamkowa

<:x
(<: x)

@fract

Mniejsze Lub Równe

<:x .y

@lte

Operatory >

Operacja Notacja Realizacja Semantyka

Liczenie Zer Końcowych

>x
(> x)

@ctz

Większe

>x .y
(> x y)

@gt

Sufit

>.x
(>. x)

@ceil

Maksymalna

>.x .y
(>. x y)

@max

Część Całkowita

>:x
(>: x)

@trunc

Większe Lub Równe

>:x .y

@gte

TODO

Sygnały Wbudowane

TODO

Dodatek A: Gramatyka

grammar.peg
# This grammar is designed for a predictive recursive-descent implementation.
# and intended to be LL(k) at the token level.
#
# In other words, whenever the parser is required to make a choice it must be
# able to choose "correctly" with constant token lookahead. This means that the
# handwritten recursive descent parser for this grammar can be implemented with
# worst-case linear run time rather than worst-case exponential runtime.
#
# All top-level syntactic decisions are bounded by token lookahead or by
# parsing a uniquely identified common prefix and then inspecting a bounded
# number of following tokens.
#
# The implementation does not perform general PEG backtracking.
# In particular, alternatives sharing a prefix are manually factored into
# the corresponding parse_* routine.
#
# See this paper for analysis of how LL(k) grammars relate to PEGs:
# https://arxiv.org/abs/1304.3177

Root <- ContainerMembers skip eof

Container <- LBRACE ContainerMembers RBRACE

ContainerMembers <- domain_doc_comment* (doc_comment* (Declaration / Descriptor / Expr))*

# *** Expressions ***

# The ;3 comments are parsed as expressions for diagnostics, but their resulting 
# nodes are discarded. Multiple expression comments may therefore occur consecutively 
# before the actual expression, being consumed before the actual expression. 

Expr <- (EXPRESSIONCOMMENT+ ExprBody)* ExprBody

ExprBody <- ExprNud ExprSuffix*

ExprSuffix <- (DOT2 / DOT3) ExprNud

ExprNud
    <- SimpleLiteral
     / MULTILINESTRINGLITERAL
     / BindingBody
     / Container
     / IndexSpace
     / PtrType
     / Signal
     / Operator
     / Polyadic
     / Assembly
     / Quote
     / Macro

ExprType <- (EXPRESSIONCOMMENT+ ExprTypeBody)* ExprTypeBody

ExprTypeBody <- (MonadicType / SignalType / ![Operator / Signal] ExprNud)

SimpleLiteral
    <- IDENTIFIER
     / BUILTIN
     / ORDINAL
     / NUMBERLITERAL
     / CHARLITERAL
     / STRINGLITERAL

# *** Signals ***

Signal <- KEYWORD_sig ((SignalMembers SignalBody) / SignalDoma / SignalLiftOp)

SignalType <- KEYWORD_sig SignalMembers ExprType

SignalDoma <- BACKSLASH KEYWORD_doma Container

SignalBody
    <- ExprType (DOT Expr)?
     / DOT Expr

SignalMembers <- LBRACE (Descriptor / Expr)* RBRACE 

SignalLiftOp <- BACKSLASH (PolyadicExtraOps / Polyadic / Operator)

# *** Pointers / Slices / Arrays ***

PtrType <- BACKSLASH Access? ExprType

IndexSpace <- LBRACKET (SlicePtrType / ArrayType / ArrayAccess)

SlicePtrType <- BACKSLASH? (COLON Expr)? RBRACKET Access? ExprType

ArrayType <- &(Expr) (COLON Expr)? RBRACKET ExprType

ArrayAccess <- (Access / Expr) Expr+ RBRACKET

# *** Invocations ***

MonadicType <- OP ExprType

# Is monadic unless continued by either dot (one more) or colon (two more).
Operator <- unclutter_operators OP Expr (DyadicTail / TriadicTail)?

DyadicTail <- DOT Expr

TriadicTail <- COLON Expr Expr

Polyadic <- LPAREN PolyadicMembers RPAREN

PolyadicExtraOps <- SimpleLiteral / COLON / DOT / DOT2 / DOT3 / Access # load/store/rmw

PolyadicMembers <- (OP / PolyadicExtraOps / ExprNud) (DescriptorStore / Expr)*

# *** Descriptors / Declarations ***

Access <- KEYWORD_rw / KEYWORD_ro / KEYWORD_wo

BindingDoma <- KEYWORD_doma ![Access]
BindingOut  <- KEYWORD_out ![KEYWORD_in]
BindingIn   <- KEYWORD_in ![KEYWORD_out]

BindingInOut
    <- KEYWORD_inout
     / (KEYWORD_in KEYWORD_out)
     / (KEYWORD_out KEYWORD_in)

Binding
    <- BindingDoma
     / BindingInOut
     / BindingOut
     / BindingIn

BindingBody <- Binding ExprType

# The descriptors participate in a domain's storage, region topology and compile-time seeds.
Descriptor <- (EXPRESSIONCOMMENT+ DescriptorBody)* DescriptorBody

# Store variant is standalone as polyadic shouldn't care about the storage variant as member. 
DescriptorBody 
    <- &(Binding* IDENTIFIER) COLON ExprType (DOT Expr)?
     / DescriptorStoreBody

DescriptorStore <- (EXPRESSIONCOMMENT+ DescriptorStoreBody)* DescriptorStoreBody

DescriptorStoreBody <- &(Binding* (IDENTIFIER / ORDINAL)) DOT Expr

# The declaration is a variable living in a region's namespace, and potentially instanced storage.
Declaration <- (EXPRESSIONCOMMENT+ DeclarationBody)* DeclarationBody

DeclarationBody <- DeclarationHeader IDENTIFIER (COLON ExprType)? DOT Expr

DeclarationHeader
    <- KEYWORD_doma Access
     / Access KEYWORD_doma?

# *** Macros ***

Quote <- BACKTICK+ QuoteTokenTree

# The `!eof .` rule must consume any non-EOF lexical token.
QuoteTokenTree
    <- QuoteUnquote
     / QuoteGroup
     / !(RPAREN / RBRACE / RBRACKET) !eof .

QuoteGroup
    <- LPAREN QuoteTokenTree* RPAREN
     / LBRACE QuoteTokenTree* RBRACE
     / LBRACKET QuoteTokenTree* RBRACKET

# Only meaningful while scanning inside a Quote;
QuoteUnquote <- COMMA IDENTIFIER

Macro <- KEYWORD_macro MacroBody

MacroBody 
    <- LBRACE domain_doc_comment* (doc_comment* MacroRules)* RBRACE
     / BACKSLASH MacroRules

MacroRules <- (EXPRESSIONCOMMENT+ MacroRulesBody)* MacroRulesBody

MacroRulesBody <- LBRACE MacroRulesDescriptor* RBRACE DOT ![Macro] Expr

MacroRulesDescriptor <- IDENTIFIER COLON BUILTIN DOT3?

# *** Inline assembly ***

# The optional identifier lists an explicit backend to consume this assembly.
Assembly <- KEYWORD_asm IDENTIFIER? LBRACE AssemblyMembers RBRACE

AssemblyMembers <- domain_doc_comment* (doc_comment* AssemblyStmt)*

AssemblyStmt <- (EXPRESSIONCOMMENT+ AssemblyStmtBody)* AssemblyStmtBody

AssemblyStmtBody
    <- AssemblyLabel
     / DOT? AssemblyInst
     / AssemblyDescriptor

AssemblyLabel <- &(IDENTIFIER) COLON AssemblyEndStmt

# Here the binding is required for storage, and store descriptors expect an assembly instruction.
AssemblyDescriptor
    <- Binding IDENTIFIER COLON ExprType (DOT Expr)? AssemblyEndStmt
     / (ORDINAL / BUILTIN) DOT AssemblyInst

AssemblyInst 
    <- IDENTIFIER (![skip_require_newline] Expr)*
     / LPAREN IDENTIFIER (DescriptorStore / Expr)* RPAREN

AssemblyEndStmt <- skip_require_newline / &(RBRACE)

# *** Tokens ***

# The source is decoded in its entirety from its external encoding into a
# stream of Unicode scalar values before lexical analysis begins.
#
# The supported source encodings are:
#   UTF-8       utf8_bom    <- '\357\273\277'       EF BB BF
#   UTF-16BE    utf16be_bom <- '\376\377'           FE FF
#   UTF-16LE    utf16le_bom <- '\377\376'           FF FE
#   UTF-32BE    utf32be_bom <- '\000\000\376\377'   00 00 FE FF
#   UTF-32LE    utf32le_bom <- '\377\376\000\000'   FF FE 00 00
#
# A source without a BOM is interpreted as UTF-8.
# https://en.wikipedia.org/wiki/Byte_order_mark
#
# Malformed source encoding is diagnosed by the normalization pass and never
# reaches the lexer or parser. On successful normalization, every element of
# the lexical input is one valid Unicode scalar value.
#
# Consequently, the lexical grammar below is completely independent of the
# source encoding and operates on Unicode scalar values rather than encoded
# bytes or code units.

# Unfortunately, there is not a standard way to match the start of the file in PEG.
# This rule definition is compatible with the original peg(1) tool but is not
# portable. It is however trivial to implement an equivalent rule in hand written
# parsers and other PEG tooling should have similar mechanisms.
#
# The normalization pass has already consumed the BOM.
sof <- &{ (yy->__pos == 0) } 
eof <- !.

# Unicode XID properties are checked by semantic predicates.
# These operate directly on the current Unicode scalar value
# supplied by the normalized source stream.
#
# unicode_xid_start <- &{ unicode_xid_start(yy) } non_control_char
# unicode_xid_continue <- &{ unicode_xid_continue(yy) } non_control_char
unicode_scalar <- [\200-(\000\016\255\255)]

unicode_xid_start <- [A-Za-z] / unicode_scalar
unicode_xid_continue <- [A-Za-z0-9] / unicode_scalar

# Exclude \177 which is DEL, but allow all other Unicode scalar values >= 0x80.
# In the normalized representation there is no distinction between ony-byte
# and multibyte source encodings anymore.
non_control_char <- [\040-\176] / unicode_scalar

digit <- [_0-9A-DF-OQ-Za-df-oq-z]
digit_int <- digit / [eEpP]
digit_float <- digit / [eEpP] [-+]?

NUMBERLITERAL
    <- skip [0-9] digit_int* &('.') digit_float+
     / skip [0-9] digit_float*

char_char
    <- '\\' non_control_char
     / !['\\] non_control_char
CHARLITERAL <- skip ['] char_char* [']

string_char
    <- '\\' non_control_char
     / !["\\] non_control_char
string <- ["] string_char* ["]
STRINGLITERAL <- skip string

line_string <- '\\\\' non_control_char* newline
MULTILINESTRINGLITERAL <- (skip line_string)+

line_comment
    <- ';' ![@;3] non_control_char* newline
     / ';;;' non_control_char* newline

# We forbid same-line doc comments to disambiguate the mapping to e.g. struct
# fields for documentation generation tooling.
doc_comment <- (sof / skip_require_newline) (skip ';;' non_control_char* newline)+
domain_doc_comment <- (skip ';@' non_control_char* newline)+

# This uses a positive lookahead rather than consuming input to make e.g.
# the newline terminating a multiline string literal or doc comment visible
# to the pre_op_white non-terminal.
newline <- &("\n" / "\r\n" / eof)
skip <- sof? ([ \n\t\r] / line_comment)*
skip_require_newline <- [ \t\r]* ([\n] / line_comment) skip

# An identifier may begin with a letter, underscore character or unicode_xid_start.
ident_begin <- '_' / unicode_xid_start

# We allow unicode and some ASCII characters as continuation in identifiers.
# These characters must not be tailing in a word, and apostrophe must be tailing instead.
ident_continue <- (op_char (unicode_xid_continue / skip)) / unicode_xid_continue

IDENTIFIER
    <- skip !keyword ident_begin ident_continue*
     / skip '@' string
BUILTIN <- skip '@' ident_begin ident_continue*

# Positional ordinal index to access anonymous bindings,
# also used with inline assembly SSA values.
ORDINAL <- skip '@' [0-9] digit_int*

# Operator tokens.
op_char <- [<>/|!?&#$%^~*=+\-]
OP <- skip op_char [.:]?

# Some whitespace rules to catch apparent "infix" expressions.
unclutter_operators
    <- ([ \n\t\r] / line_comment)+
     / &(RPAREN / LBRACE / RBRACE / LBRACKET / RBRACKET) 
     / &(OP / DOT / DOT2 / DOT3 / COLON / COMMA / BACKTICK / BACKSLASH)

EXPRESSIONCOMMENT           <- skip ';3'
BACKSLASH                   <- skip '\\'
BACKTICK                    <- skip '`'
COMMA                       <- skip ','
COLON                       <- skip ':'
DOT                         <- skip '.'     ![.]
DOT2                        <- skip '..'    ![.]
DOT3                        <- skip '...'
LPAREN                      <- skip '('
RPAREN                      <- skip ')'
LBRACE                      <- skip '{'
RBRACE                      <- skip '}'
LBRACKET                    <- skip '['
RBRACKET                    <- skip ']'

end_of_word <- !ident_continue
KEYWORD_asm     <- skip 'asm' end_of_word
KEYWORD_doma    <- skip 'doma' end_of_word
KEYWORD_in      <- skip 'in' end_of_word
KEYWORD_inout   <- skip 'inout' end_of_word
KEYWORD_out     <- skip 'out' end_of_word
KEYWORD_macro   <- skip 'macro' end_of_word
KEYWORD_ro      <- skip 'ro' end_of_word
KEYWORD_rw      <- skip 'rw' end_of_word
KEYWORD_sig     <- skip 'sig' end_of_word
KEYWORD_wo      <- skip 'wo' end_of_word

keyword
    <- KEYWORD_asm 
     / KEYWORD_doma
     / KEYWORD_in 
     / KEYWORD_inout 
     / KEYWORD_out 
     / KEYWORD_macro
     / KEYWORD_ro 
     / KEYWORD_rw 
     / KEYWORD_sig 
     / KEYWORD_wo