diff options
Diffstat (limited to 'docs/book/SYNTAX.html')
-rw-r--r-- | docs/book/SYNTAX.html | 330 |
1 files changed, 247 insertions, 83 deletions
diff --git a/docs/book/SYNTAX.html b/docs/book/SYNTAX.html index 6aa71e8..a241b74 100644 --- a/docs/book/SYNTAX.html +++ b/docs/book/SYNTAX.html @@ -7,7 +7,7 @@ <!-- Custom HTML head --> - + <meta name="description" content=""> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="theme-color" content="#ffffff"> @@ -174,40 +174,212 @@ <div id="content" class="content"> <main> <h1 id="syntax-a-casual-and-formal-look"><a class="header" href="#syntax-a-casual-and-formal-look">Syntax: A Casual and Formal Look</a></h1> -<blockquote> -<p>! This section is <strong>incomplete</strong>. Proceed with caution.</p> -</blockquote> +<h2 id="call-syntax"><a class="header" href="#call-syntax">Call Syntax</a></h2> +<p>There is little difference between a function, macro, and operator call. There are only a few forms such calls can take, too, though notably more than most other languages (due to, among other things, uniform function call syntax): hence this section.</p> +<pre><code># The standard, unambiguous call. +routine(1, 2, 3, 4) +# The method call syntax equivalent. +1.routine(2, 3, 4) +# A block-based call. This is only really useful for macros taking in a body. +routine + 1 + 2 + 3 + 4 +# A parentheses-less call. This is only really useful for `print` and `dbg`. +# Only valid at the start of a line. +routine 1, 2, 3, 4 +</code></pre> +<p>Binary operators have some special rules.</p> +<pre><code># Valid call syntaxes for binary operators. What can constitute a binary +# operator is constrained for parsing's sake. Whitespace is optional. +1 + 2 +1+2 ++ 1, 2 # Only valid at the start of a line. Also, don't do this. ++(1, 2) +</code></pre> +<p>As do unary operators.</p> +<pre><code># The standard call for unary operators. Postfix. +1? +?(1) +</code></pre> +<p>Method call syntax has a number of advantages: notably that it can be <em>chained</em>: acting as a natural pipe operator. Redundant parenthesis can also be omitted.</p> +<pre><code># The following statements are equivalent: +foo.bar.baz +foo().bar().baz() +baz(bar(foo)) +baz + bar + foo +baz bar(foo) +baz foo.bar +</code></pre> +<h2 id="indentation-rules"><a class="header" href="#indentation-rules">Indentation Rules</a></h2> +<p>The tokens <code>=</code>, <code>then</code>, <code>do</code>, <code>of</code>, <code>else</code>, <code>block</code>, <code>const</code>, <code>block X</code>, and <code>X</code> (where <code>X</code> is an identifier) are <em>scope tokens</em>. They denote a new scope for their associated expressions (functions/macros/declarations, control flow, loops). The tokens <code>,</code>, <code>.</code> (notably not <code>...</code>), and all default binary operators (notably not <code>not</code>) are <em>continuation tokens</em>. An expression beginning or ending in one of them would always be a syntactic error.</p> +<p>Line breaks are treated as the end of a statement, with several exceptions.</p> +<pre><code class="language-puck">pub func foo() = + print "Hello, world!" + print "This is from a function." + +pub func inline_decl() = print "Hello, world!" +</code></pre> +<p>Indented lines following a line ending in a <em>scope token</em> are treated as belonging to a new scope. That is, indented lines following a line ending in a scope token form the body of the expression associated with the scope token.</p> +<p>Indentation is not obligatory after a scope token. However, this necessarily constrains the body of the associated expression to one line: no lines following will be treated as an extension of the body, only the expression associated with the original scope token. (This may change in the future.)</p> +<pre><code class="language-puck">pub func foo(really_long_parameter: ReallyLongType, +another_really_long_parameter: AnotherReallyLongType) = # no indentation! this is ok + print really_long_parameter # this line is indented relative to the first line + print really_long_type +</code></pre> +<p>Lines following a line ending in a <em>continuation token</em> (and, additionally <code>not</code> and <code>(</code>) are treated as a continuation of that line and can have any level of indentation (even negative). If they end in a scope token, however, the following lines must be indented relative to the indentation of the previous line.</p> +<pre><code class="language-puck">let really_long_parameter: ReallyLongType = ... +let another_really_long_parameter: AnotherReallyLongType = ... + +really_long_parameter + .foo(another_really_long_parameter) # some indentation! this is ok +</code></pre> +<p>Lines <em>beginning</em> in a continuation token (and, additionally <code>)</code>), too, are treated as a continuation of the previous line and can have any level of indentation. If they end in a scope token, the following lines must be indented relative to the indentation of the previous line.</p> +<pre><code class="language-puck">pub func foo() = + print "Hello, world!" +pub func bar() = # this line is no longer in the above scope. + print "Another function declaration." +</code></pre> +<p>Dedented lines <em>not</em> beginning or ending with a continuation token are treated as no longer in the previous scope, returning to the scope of the according indentation level.</p> +<pre><code class="language-puck">if cond then this +else that + +match cond +of this then ... +of that then ... +</code></pre> +<p>A line beginning with a scope token is treated as attached to the previous expression.</p> +<pre><code># Technically allowed. Please don't do this. +let foo += ... + +if cond then if cond then this +else that + +for i +in iterable +do ... + +match foo of this then ... +of that then ... + +match foo of this +then ... +of that then ... +</code></pre> +<p>This <em>can</em> lead to some ugly possibilities for formatting that are best avoided.</p> +<pre><code># Much preferred. + +let foo = + ... +let foo = ... + +if cond then + if cond then + this +else that +if cond then + if cond then this +else that + +for i in iterable do + ... +for i in iterable do ... + +match foo +of this then ... +of that then ... +</code></pre> +<p>The indentation rules are complex, but the effect is such that long statements can be broken <em>almost</em> anywhere.</p> +<h2 id="expression-rules"><a class="header" href="#expression-rules">Expression Rules</a></h2> +<p>First, a word on the distinction between <em>expressions</em> and <em>statements</em>. Expressions return a value. Statements do not. That is all.</p> +<p>There are some syntactic constructs unambiguously recognizable as statements: all declarations, modules, and <code>use</code> statements. There are no syntactic constructs unambiguously recognizable as expressions. As calls returning <code>void</code> are treated as statements, and expressions that return a type could possibly return <code>void</code>, there is no explicit distinction between expressions and statements made in the parser: or anywhere before type-checking.</p> +<p>Expressions can go almost anywhere. Our indentation rules above allow for it.</p> +<pre><code># Some different formulations of valid expressions. + +if cond then + this +else + that + +if cond then this +else that + +if cond +then this +else that + +if cond then this else that + +let foo = + if cond then + this + else + that +</code></pre> +<pre><code># Some different formulations of *invalid* expressions. +# These primarily break the rule that everything following a scope token +# (ex. `=`, `do`, `then`) not at the end of the line must be self-contained. + +let foo = if cond then + this + else + that + +let foo = if cond then this + else that + +let foo = if cond then this +else that + +# todo: how to handle this? +if cond then if cond then that +else that + +# shrimple +if cond then + if cond then that +else that + +# this should be ok +if cond then this +else that + +match foo of +this then ... +of that then ... +</code></pre> <h2 id="reserved-keywords"><a class="header" href="#reserved-keywords">Reserved Keywords</a></h2> <p>The following keywords are reserved:</p> <ul> <li>variables: <code>let</code> <code>var</code> <code>const</code></li> -<li>control flow: <code>if</code> <code>elif</code> <code>else</code></li> +<li>control flow: <code>if</code> <code>then</code> <code>elif</code> <code>else</code></li> <li>pattern matching: <code>match</code> <code>of</code></li> -<li>loops: <code>loop</code> <code>while</code> <code>for</code> <code>in</code></li> -<li>blocks: <code>block</code> <code>break</code> <code>continue</code> <code>return</code></li> -<li>functions: <code>func</code> <code>mut</code> <code>static</code> <code>varargs</code></li> +<li>error handling: <code>try</code> <code>with</code> <code>finally</code></li> +<li>loops: <code>while</code> <code>do</code> <code>for</code> <code>in</code></li> +<li>blocks: <code>loop</code> <code>block</code> <code>break</code> <code>continue</code> <code>return</code></li> <li>modules: <code>pub</code> <code>mod</code> <code>use</code> <code>as</code></li> -<li>error handling: <code>try</code> <code>catch</code> <code>finally</code></li> +<li>functions: <code>func</code> <code>varargs</code></li> <li>metaprogramming: <code>macro</code> <code>quote</code> <code>when</code></li> -<li>types: <code>type</code> <code>distinct</code> <code>ref</code></li> -<li>types: <code>struct</code> <code>tuple</code> <code>union</code> <code>enum</code> <code>interface</code></li> -<li>reserved: +<li>ownership: <code>lent</code> <code>mut</code> <code>ref</code> <code>refc</code></li> +<li>types: <code>type</code> <code>struct</code> <code>tuple</code> <code>union</code> <code>enum</code> <code>class</code></li> +</ul> +<p>The following keywords are not reserved, but liable to become so.</p> <ul> -<li><code>impl</code> <code>object</code> <code>class</code> <code>concept</code> <code>auto</code> <code>empty</code> <code>effect</code> <code>case</code></li> -<li><code>suspend</code> <code>resume</code> <code>spawn</code> <code>pool</code> <code>thread</code> <code>closure</code></li> +<li><code>impl</code> <code>object</code> <code>interface</code> <code>concept</code> <code>auto</code> <code>effect</code> <code>case</code></li> +<li><code>suspend</code> <code>resume</code> <code>spawn</code> <code>pool</code> <code>thread</code> <code>closure</code> <code>static</code></li> <li><code>cyclic</code> <code>acyclic</code> <code>sink</code> <code>move</code> <code>destroy</code> <code>copy</code> <code>trace</code> <code>deepcopy</code></li> </ul> -</li> -</ul> <p>The following identifiers are in use by the standard prelude:</p> <ul> <li>logic: <code>not</code> <code>and</code> <code>or</code> <code>xor</code> <code>shl</code> <code>shr</code> <code>div</code> <code>mod</code> <code>rem</code></li> <li>logic: <code>+</code> <code>-</code> <code>*</code> <code>/</code> <code><</code> <code>></code> <code><=</code> <code>>=</code> <code>==</code> <code>!=</code> <code>is</code></li> <li>async: <code>async</code> <code>await</code></li> -<li>types: <code>int</code> <code>uint</code> <code>float</code> +<li>types: <code>int</code> <code>uint</code> <code>float</code> <code>i\d+</code> <code>u\d+</code> <ul> -<li><code>i8</code> <code>i16</code> <code>i32</code> <code>i64</code> <code>i128</code></li> -<li><code>u8</code> <code>u16</code> <code>u32</code> <code>u64</code> <code>u128</code></li> <li><code>f32</code> <code>f64</code> <code>f128</code></li> <li><code>dec64</code> <code>dec128</code></li> </ul> @@ -220,23 +392,28 @@ <ul> <li><code>=</code> (assignment)</li> <li><code>.</code> (chaining)</li> -<li><code>,</code> (params)</li> +<li><code>,</code> (parameters)</li> <li><code>;</code> (statements)</li> <li><code>:</code> (types)</li> <li><code>#</code> (comment)</li> +<li><code>@</code> (attributes)</li> <li><code>_</code> (unused bindings)</li> <li><code>|</code> (generics)</li> <li><code>\</code> (string/char escaping)</li> -<li><code>()</code> (params, tuples)</li> -<li><code>{}</code> (scope, structs)</li> +<li><code>()</code> (parameters, tuples)</li> <li><code>[]</code> (generics, lists)</li> -<li><code>""</code> (strings)</li> +<li><code>{}</code> (scope, structs)</li> +<li><code>""</code> (strings)</li> <li><code>''</code> (chars)</li> <li><code>``</code> (unquoting)</li> -<li>unused: <code>~</code> <code>@</code> <code>$</code> <code>%</code></li> +<li>unused on qwerty: <code>~</code> <code>%</code> <code>^</code> <code>$</code> +<ul> +<li>perhaps leave <code>$</code> unused. but <code>~</code>, <code>%</code>, and <code>^</code> totally could be...</li> +</ul> +</li> </ul> <h2 id="a-formal-grammar"><a class="header" href="#a-formal-grammar">A Formal Grammar</a></h2> -<p>We now shall take a look at a more formal description of Puck's syntax. </p> +<p>We now shall take a look at a more formal description of Puck's syntax.</p> <p>Syntax rules are described in <a href="https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form">extended Backus–Naur form</a> (EBNF): however, most rules surrounding whitespace, and scope, and line breaks, are modified to how they would appear after a lexing step.</p> <h3 id="identifiers"><a class="header" href="#identifiers">Identifiers</a></h3> <pre><code>Ident ::= (Letter | '_') (Letter | Digit | '_')* @@ -258,81 +435,84 @@ HexDigit ::= Digit | 'A'..'F' | 'a'..'f' <pre><code>CHAR ::= '\'' (PRINT - '\'' | '\\\'')* '\'' STRING ::= SINGLE_LINE_STRING | MULTI_LINE_STRING COMMENT ::= SINGLE_LINE_COMMENT | MULTI_LINE_COMMENT | EXPRESSION_COMMENT -SINGLE_LINE_STRING ::= '"' (PRINT - '"' | '\\"')* '"' -MULTI_LINE_STRING ::= '"""' (PRINT | '\n' | '\r')* '"""' +SINGLE_LINE_STRING ::= '"' (PRINT - '"' | '\\"')* '"' +MULTI_LINE_STRING ::= '"""' (PRINT | '\n' | '\r')* '"""' SINGLE_LINE_COMMENT ::= '#' PRINT* MULTI_LINE_COMMENT ::= '#[' (PRINT | '\n' | '\r' | MULTI_LINE_COMMENT)* ']#' EXPRESSION_COMMENT ::= '#;' SINGLE_STMT PRINT ::= LETTER | DIGIT | OPR | - '"' | '#' | "'" | '(' | ')' | # notably the dual of OPR + '"' | '#' | "'" | '(' | ')' | # notably the dual of OPR ',' | ';' | '[' | ']' | '_' | '`' | '{' | '}' | ' ' | '\t' </code></pre> <h3 id="values"><a class="header" href="#values">Values</a></h3> <pre><code>Value ::= Int | Float | String | Char | Array | Tuple | Struct Array ::= '[' (Expr (',' Expr)*)? ']' -Tuple ::= '(' (Ident ':')? Expr (',' (Ident ':')? Expr)* ')' -Struct ::= '{' Ident ':' Expr (',' Ident ':' Expr)* '}' +Tuple ::= '(' (Ident '=')? Expr (',' (Ident '=')? Expr)* ')' +Struct ::= '{' Ident '=' Expr (',' Ident '=' Expr)* '}' </code></pre> <h3 id="variables"><a class="header" href="#variables">Variables</a></h3> <pre><code>Decl ::= Let | Var | Const | Func | Type -Let ::= 'let' Pattern Annotation? '=' Expr -Var ::= 'var' Pattern Annotation? ('=' Expr)? -Const ::= 'pub'? 'const' Pattern Annotation? '=' Expr -Pattern ::= Char | String | Number | Float | Ident | '(' Pattern (',' Pattern)* ')' - Ident '(' Pattern (',' Pattern)* ')' +Let ::= 'let' Pattern (':' Type)? '=' Expr +Var ::= 'var' Pattern (':' Type)? ('=' Expr)? +Const ::= 'pub'? 'const' Pattern (':' Type)? '=' Expr +Pattern ::= (Ident ('as' Ident)?) | Char | String | Number | Float | + Ident? '(' Pattern (',' Pattern)* ')' </code></pre> <h3 id="declarations"><a class="header" href="#declarations">Declarations</a></h3> -<pre><code>Func ::= 'pub'? 'func' Ident Generics? Parameters? Annotation? '=' Body -Macro ::= 'pub'? 'macro' Ident Generics? Parameters? Annotation? '=' Body -Generics ::= '[' Ident Annotation? (',' Ident Annotation?)* ']' -Parameters ::= '(' Ident Annotation? (',' Ident Annotation?)* ')' -Annotation ::= ':' Type +<pre><code>Func ::= 'pub'? 'func' Ident Generics? Parameters? (':' Type)? '=' Body +Macro ::= 'pub'? 'macro' Ident Generics? Parameters? (':' Type)? '=' Body +Generics ::= '[' Ident (':' Type)? (',' Ident (':' Type)?)* ']' +Parameters ::= '(' Ident (':' Type)? (',' Ident (':' Type)?)* ')' </code></pre> +<p>All arguments to functions must have a type. This is resolved at the semantic level, however. (Arguments to macros may lack types. This signifies a generic node.)</p> <h3 id="types"><a class="header" href="#types">Types</a></h3> <pre><code>TypeDecl ::= 'pub'? 'type' Ident Generics? '=' Type -Type ::= StructType | TupleType | EnumType | UnionType | Interface | - (('distinct' | 'ref' | 'ptr' | 'mut' | 'static') (Type | ('[' Type ']'))?) -StructType ::= 'struct' ('[' Ident ':' Type (',' Ident ':' Type)* ']')? -UnionType ::= 'union' ('[' Ident ':' Type (',' Ident ':' Type)* ']')? -TupleType ::= 'tuple' ('[' (Ident ':')? Type (',' (Ident ':')? Type)* ']')? -EnumType ::= 'enum' ('[' Ident ('=' Expr)? (',' Ident ('=' Expr)?)* ']')? -Interface ::= 'interface' ('[' Signature (',' Signature)* ']')? -Signature ::= Ident Generics? ('(' Type (',' Type)* ')')? Annotation? +Type ::= TypeStruct | TypeTuple | TypeEnum | TypeUnion | SugarUnion | + TypeClass | (Modifier* (Type | ('[' Type ']'))) +TypeStruct ::= 'struct' ('[' Ident ':' Type (',' Ident ':' Type)* ']')? +TypeUnion ::= 'union' ('[' Ident ':' Type (',' Ident ':' Type)* ']')? +SugarUnion ::= '(' Ident ':' Type (',' Ident ':' Type)* ')' +TypeTuple ::= 'tuple' ('[' (Ident ':')? Type (',' (Ident ':')? Type)* ']')? +TypeEnum ::= 'enum' ('[' Ident ('=' Expr)? (',' Ident ('=' Expr)?)* ']')? +TypeClass ::= 'class' ('[' Signature (',' Signature)* ']')? +Modifier ::= 'ref' | 'refc' | 'ptr' | 'lent' | 'mut' | 'const' +Signature ::= Ident Generics? ('(' Type (',' Type)* ')')? (':' Type)? </code></pre> <h2 id="control-flow"><a class="header" href="#control-flow">Control Flow</a></h2> -<pre><code>If ::= 'if' Expr ':' Body ('elif' Expr ':' Body)* ('else' ':' Body)? -When ::= 'when' Expr ':' Body ('elif' Expr ':' Body)* ('else' ':' Body)? -Try ::= 'try' ':' Body - ('except' Ident ('as' Ident)? (',' Ident ('as' Ident)?)*) ':' Body)* - ('finally' ':' Body)? -Match ::= 'match' Expr ('of' Pattern (',' Pattern)* ('where' Expr)? ':' Body)+ -Block ::= 'block' Ident? ':' Body -Block ::= 'static' ':' Body -Loop ::= 'loop' ':' Body -While ::= 'while' Expr ':' Body -For ::= 'for' Pattern 'in' Expr Body +<pre><code>If ::= 'if' Expr 'then' Body ('elif' Expr 'then' Body)* ('else' Body)? +When ::= 'when' Expr 'then' Body ('elif' Expr 'then' Body)* ('else' Body)? +Try ::= 'try' Body + ('except' Ident ('as' Ident)? (',' Ident ('as' Ident)?)*) 'then' Body)+ + ('finally' Body)? +Match ::= 'match' Expr ('of' Pattern (',' Pattern)* ('where' Expr)? 'then' Body)+ +While ::= 'while' Expr 'do' Body +For ::= 'for' Pattern 'in' Expr 'do' Body +Loop ::= 'loop' Body +Block ::= 'block' Ident? Body +Const ::= 'const' Body +Quote ::= 'quote' QuoteBody </code></pre> <h2 id="modules"><a class="header" href="#modules">Modules</a></h2> -<pre><code>Mod ::= 'pub'? 'mod' Ident ':' Body -Use ::= 'use' Ident ('/' Ident)* ('/' ('[' Ident (',' Ident)* ']'))? +<pre><code>Mod ::= 'pub'? 'mod' Ident '=' Body +Use ::= 'use' Ident ('.' Ident)* ('.' ('[' Ident (',' Ident)* ']'))? </code></pre> <h3 id="operators"><a class="header" href="#operators">Operators</a></h3> <pre><code>Operator ::= 'and' | 'or' | 'not' | 'xor' | 'shl' | 'shr' | - 'div' | 'mod' | 'rem' | 'is' | 'in' | - Opr+ + 'div' | 'mod' | 'rem' | 'is' | 'in' | Opr+ Opr ::= '=' | '+' | '-' | '*' | '/' | '<' | '>' | '@' | '$' | '~' | '&' | '%' | '|' | '!' | '?' | '^' | '.' | ':' | '\\' </code></pre> <h2 id="calls-and-expressions"><a class="header" href="#calls-and-expressions">Calls and Expressions</a></h2> +<p>This section is (quite) inaccurate due to complexities with respect to significant indentation. Heed caution.</p> <pre><code>Call ::= Ident ('[' Call (',' Call)* ']')? ('(' (Ident '=')? Call (',' (Ident '=')? Call)* ')')? | Ident Call (',' Call)* | Call Operator Call? | - Call ':' Body -Expr ::= Let | Var | Const | Func | Type | Mod | Use | Block | Static | - For | While | Loop | If | When | Try | Match | Call -Body ::= Expr | ('{' Expr (';' Expr)* '}') + Call Body +Stmt ::= Let | Var | Const | Func | Type | Mod | Use | Expr +Expr ::= Block | Const | For | While | Loop | If | When | Try | Match | Call +Body ::= (Stmt ';')* Expr </code></pre> <hr /> <p>References:</p> @@ -372,22 +552,6 @@ Body ::= Expr | ('{' Expr (';' Expr)* '}') </div> - <!-- Livereload script (if served using the cli tool) --> - <script> - const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:'; - const wsAddress = wsProtocol + "//" + location.host + "/" + "__livereload"; - const socket = new WebSocket(wsAddress); - socket.onmessage = function (event) { - if (event.data === "reload") { - socket.close(); - location.reload(); - } - }; - - window.onbeforeunload = function() { - socket.close(); - } - </script> |