aboutsummaryrefslogtreecommitdiff
path: root/docs/book/SYNTAX.html
diff options
context:
space:
mode:
Diffstat (limited to 'docs/book/SYNTAX.html')
-rw-r--r--docs/book/SYNTAX.html330
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>&lt;</code> <code>&gt;</code> <code>&lt;=</code> <code>&gt;=</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>&quot;&quot;</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 ::= '&quot;' (PRINT - '&quot;' | '\\&quot;')* '&quot;'
-MULTI_LINE_STRING ::= '&quot;&quot;&quot;' (PRINT | '\n' | '\r')* '&quot;&quot;&quot;'
+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 |
- '&quot;' | '#' | &quot;'&quot; | '(' | ')' | # 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 ::= '=' | '+' | '-' | '*' | '/' | '&lt;' | '&gt;' |
'@' | '$' | '~' | '&amp;' | '%' | '|' |
'!' | '?' | '^' | '.' | ':' | '\\'
</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>