Syntax
Here are the combined syntactical and lexical rules for the SkookumScript grammar in modified Extended Backus-Naur Form (EBNF) – essentially a cheat sheet for the language.
EBNF is by no means a manual - though it is nice to have and some language geeks (such as the SkookumScript Team) will appreciate it.
Grammar Key
- Production rules: in-italics
- Terminals:
inset-border - Literal strings: ‘
quoted with inset border’ - Optional groups: [ ]
- Repeating groups of zero or more: { }
- Repeating groups of n or more: { }n+
- Mandatory groups: ( )
- Alternatives: |
Many programming languages try to keep their grammar context free as much as possible so that later code does not need to depend on earlier code – this makes it easier to write a compiler. SkookumScript is the opposite – its grammar is context rich and tries to have the compiler figure out things as you would sensibly expect.
This means that many production rules cannot be simply described with standard EBNF – and in those cases a footnote1 is used after the name of a production-rule to describe additional details for how the grammar works depending on the context.
SkookumScript Language Specification
| Expressions | ||
|---|---|---|
| expression | = | literal | identifier | variable-primitive | invocation | type-primitive | flow-control |
| Literals | ||
|---|---|---|
| literal | = | boolean-literal | integer-literal | real-literal | string-literal | symbol-literal | char-literal | list-literal | closure |
| boolean-literal | = | ‘true’ | ‘false’ |
| integer-literal2 | = | simple-integer [‘r’ {big-digit}1+ |
| simple-integer3 | = | [’-’] digits |
| real-literal4 | = | simple-integer [’.’ {digit}1+] [real-exponent] |
| real-exponent | = | ‘E’ | ‘e’ simple-integer |
| string-literal | = | simple-string {ws ‘+’ ws simple-string} |
| simple-string | = | ’"’ {character} ‘"’ |
| symbol-literal | = | ’'’ {character}0-255 ‘'’ |
| character-literal | = | ‘\`’ character |
| list-literal5 | = | [list-class constructor-name invocation-args] ‘ {’ ws [expression {ws [‘,’ ws] expression} ws ‘}’ |
| closure6 | = | [’^’ {annotation ws} [‘_’ ws] [expression ws]] [parameters ws] code-block |
| Identifiers | ||
|---|---|---|
| identifier7 | = | variable-identifier | reserved-identifier | class-name | object-id |
| variable-identifier8 | = | variable-name | ([expression ws ‘.’ ws] data-name) |
| instance-name | = | lowercase {alphanumeric} |
| variable-name | = | name-predicate |
| data-name9 | = | ’@’ | ‘@@’ variable-name |
| reserved-identifier | = | ‘nil’ | ‘this’ | ‘this_class’ | ‘this_code’ | ‘this_mind’ |
| object-id10 | = | [class-name] ‘@’ [’?’ | ‘#’] symbol-literal |
| class-name | = | uppercase {alphanumeric} |
| routine-name | = | method-name | coroutine-name |
| method-name11 | = | name-predicate | constructor-name | destructor-name | class-name |
| name-predicate12 | = | instance-name [’?’] |
| constructor-name | = | ’!’ [instance-name] |
| destructor-name13 | = | ’!!’ |
| coroutine-name | = | ‘_’ instance-name |
| Variable Primitives | ||
|---|---|---|
| variable-primitive | = | create-temporary | bind |
| create-temporary14 | = | define-temporary [ws binding] |
| define-temporary | = | ’!’ ws variable-name |
| bind15 | = | variable-identifier ws binding |
| binding | = | ’:’ ws expression |
| Invocations | ||
|---|---|---|
| invocation | = | invoke-call | instantiation | invoke-cascade | invoke-operator | index-operator | apply-operator |
| invoke-call16 | = | ([expression ws ‘.’ ws] invoke-selector) | operator-call |
| instantiation17 | = | [class-instance] | expression ‘!’ [instance-name] invocation-args |
| invoke-cascade | = | expression ws ‘.’ ws ‘[’ {ws invoke-selector | operator-selector}2+ ws ‘]’ |
| invoke-operator18 | = | expression bracketed-args |
| index-operator19 | = | expression ‘{’ ws expression ws ‘}’ [ws binding] |
| apply-operator20 | = | expression ws ‘%’ | ‘%>’ invoke-selector |
| invoke-selector | = | [scope] routine-name invocation-args |
| scope | = | class-name ‘@’ |
| operator-call21 | = | (prefix-operator ws expression) | (expression ws operator-selector) |
| operator-selector | = | postfix-operator | (binary-operator ws expression) |
| binary-operator | = | math-operator | compare-op | logical-operator | ‘:=’ |
| math-operator22 | = | ’+’ | ‘+=’ | ‘-’ | ‘-=’ | ‘*’ | ‘*=’ | ‘/’ | ‘/=’ |
| compare-op | = | ’=’ | ‘~=’ | ‘>’ | ‘>=’ | ‘<’ | ‘<=’ |
| logical-operator | = | ‘and’ | ‘or’ | ‘xor’ | ‘nand’ | ‘nor’ | ‘nxor’ |
| prefix-operator23 | = | ‘not’ | ‘-’ |
| postfix-operator | = | ’++’ | ‘--’ |
| invocation-args24 | = | [bracketed-args] | closure-tail-args |
| bracketed-args | = | ’(’ ws [send-args ws] [’;’ ws return-args ws] ‘)’ |
| closure-tail-args25 | = | ws send-args ws closure [ws ‘;’ ws return-args] |
| send-args | = | [argument] {ws [’,’ ws] [argument]} |
| return-args | = | [return-arg] {ws [’,’ ws] [return-arg]} |
| argument | = | [named-spec ws] expression |
| return-arg26 | = | [named-spec ws] variable-identifier | define-temporary |
| named-spec27 | = | variable-name ws ‘:’ |
| Type Primitives | ||
|---|---|---|
| type-primitive | = | class-cast | class-conversion |
| class-cast28 | = | expression ws ‘<>’ [class-desc] |
| class-conversion29 | = | expression ws ‘>>’ [class-name] |
| Flow Control | ||
|---|---|---|
| flow-control | = | code-block | if | when | unless | case | nil-coalescing | loop | loop-exit | concurrent-block |
| code-block | = | ’[’ ws [expression {wsr expression} ws] ‘]’ |
| if | = | ‘if’ {ws expression ws code-block}1+ [ws else-block] |
| else-block | = | ‘else’ ws code-block |
| when | = | expression ws ‘when’ ws expression |
| unless | = | expression ws ‘unless’ ws expression |
| case | = | ‘case’ ws expression {ws expression ws code-block}1+ [ws else-block] |
| nil-coalescing30 | = | expression ws ‘??’ ws expression |
| loop31 | = | ‘loop’ [ws instance-name] ws code-block |
| loop-exit32 | = | ‘exit’ [ws instance-name] |
| concurrent-block | = | sync | race | branch | change-mind |
| sync33 | = | ‘sync’ ws code-block |
| race34 | = | ‘race’ ws code-block |
| branch35 | = | ‘branch’ ws expression |
| change-mind36 | = | ‘change’ ws expression ws expression |
| File Contents | ||
|---|---|---|
| method-filename37 | = | method-name ‘()’ [‘C’] ‘.sk’ |
| method-file38 | = | ws {annotation ws} parameters [ws code-block] ws |
| coroutine-filename | = | coroutine-name ‘()’ ‘.sk’ |
| coroutine-file39 | = | ws {annotation ws} parameter-list [ws code-block] ws |
| data-filename40 | = | ’!Data’ [‘C’] ‘.sk’ |
| data-file | = | ws [data-definition {wsr data-definition} ws] |
| data-definition41 | = | {annotation ws} [class-desc wsr] ‘!’ data-name |
| annotation42 | = | ’&’ instance-name |
| Parameters | ||
|---|---|---|
| parameters43 | = | parameter-list [ws class-desc] |
| parameter-list | = | ’(’ ws [send-params ws] [’;’ ws return-params ws] ‘)’ |
| send-params | = | parameter {ws [’,’ ws] parameter} |
| return-params | = | param-specifier {ws [’,’ ws] param-specifier} |
| parameter | = | unary-param | group-param |
| unary-param44 | = | param-specifier [ws binding] |
| param-specifier45 | = | [class-desc wsr] variable-name |
| group-param46 | = | ’{’ ws [class-desc {wsr class-desc} ws] ‘}’ ws instance-name |
| Class Descriptors | ||
|---|---|---|
| class-desc | = | class-unary | class-union |
| class-unary | = | class-instance | meta-class |
| class-instance | = | class-name | list-class | invoke-class |
| meta-class | = | ’<’ class-name ‘>’ |
| class-union47 | = | ’<’ class-unary {‘|’ class-unary}1+ ‘>’ |
| invoke-class48 | = | [‘_’ | ‘+’] parameters |
| list-class49 | = | List ‘{’ ws [class-desc ws] ‘}’ |
| Whitespace | ||
|---|---|---|
| wsr50 | = | {whitespace}1+ |
| ws | = | {whitespace} |
| whitespace | = | whitespace-char | comment |
| whitespace-char | = | ’ ’ | formfeed | newline | carriage-return | horiz-tab | vert-tab |
| end-of-line | = | newline | carriage-return | end-of-file |
| comment | = | single-comment | multi-comment |
| single-comment | = | ’//’ {printable} end-of-line |
| multi-comment | = | ’/*’ {printable} [multi-comment {printable}] ‘*/’ |
| Characters & Digits | ||
|---|---|---|
| character | = | escape-sequence | printable |
| escape-sequence51 | = | ’\’ integer-literal | printable |
| alphanumeric | = | alphabetic | digit | ‘_’ |
| alphascore | = | alphabetic | ‘_’ |
| alphabetic | = | uppercase | lowercase |
| lowercase | = | ‘a’ | … | ‘z’ |
| uppercase | = | ‘A’ | … | ‘Z’ |
| digits | = | (non-zero-digit {digit}) | ‘0’ |
| non-zero-digit | = | ‘1’ | ‘2’ | ‘3’ | ‘4’ | ‘5’ | ‘6’ | ‘7’ | ‘8’ | ‘9’ |
| digit | = | ‘0’ | ‘1’ | ‘2’ | ‘3’ | ‘4’ | ‘5’ | ‘6’ | ‘7’ | ‘8’ | ‘9’ |
| big-digit | = | digit | alphabetic |
Download SkookumScript Syntax
Display or print out the SkookumScript syntax as a quick reference / cheat sheet. Use it when programming with SkookumScript for your project that will be the envy of the interwebs and lead to eventual world domination.
Download the SkookumScript syntax (PDF) for your very own.
Planned and Future Syntax
While SkookumScript is very state-of-the-art and the “best compared to other leading brands”™ – it continues to evolve. If you care to take a peek behind the curtain you can see some of the new syntax planned for SkookumScript in the near future.
Download the planned SkookumScript syntax (PDF) for fun and profit.
-
Here is an example footnote. ↩
-
‘
r’ indicates (r)adix / base – default 10 (decimal) if omitted. Ex:2rbinary &16rhex. big-digit(s) vary by the radix used. ↩ -
Negative simple-integer can be confused with subtract math-operator. ↩
-
Decimal part of real-literal is optional if
Realtype can be inferred from context or if the exponent part is present. ↩ -
Listtype specified if the optional list-class and appropriate constructor-name is supplied (also see instantiation). If the type is not supplied, then a type is inferred using the initial items – if there are no initial items thenObjectused as default item type). ↩ -
[Also known as: code block, anonymous function or lambda expression.] Optional ‘
^’ or parameters or both must be provided (unless used in closure-tail-args where both are optional). Optional expression (which may not be code-block or closure) will be captured and used as receiver/this for the code-block – if not providedthisis inferred. Optional ‘_’ indicates it is durational (like a coroutine) – if not present durational/immediate inferred via code-block. Parameter types, return type, scope, whether surroundingthisor temporary/parameter variables are used and captured may all be inferred if omitted. ↩ -
Note that scoping is not necessary – instance names may not be overridden and classes and implicit identifiers effectively have global scope. ↩
-
Optional expression can be used to access data member from an object – if omitted,
thisis inferred. ↩ -
’
@’ indicates instance data member and ‘@@’ indicates class instance data member. ↩ -
If class-name absent
Actorinferred or desired type if known. If optional ‘?’ is present and object not found at runtime then result isnilelse assertion error occurs. Optional hash mark ‘#’ indicates no lookup - just return name identifier validated by class type. ↩ -
A method using class-name allows explicit conversion similar to class-conversion except that the method is always called. ↩
-
Optional ‘
?’ used as convention to indicate predicate variable or method of return typeBoolean(trueorfalse). ↩ -
Destructor calls are only valid in the scope of another destructor’s code-block. ↩
-
Only valid in the scope of a code-block. ↩
-
May not be used as an argument. Only a named-spec is valid as an argument which looks similar. ↩
-
If an invoke-call’s optional expression (the receiver) is omitted, ‘
this.’ is implicitly inferred. ↩ -
If class-instance can be inferred then it may be omitted. When expression used rather than class-instance lots of syntactic sugar is provided:
expr!ctor()is alias forExprClass!ctor(expr)– ex:num!copyequalsInteger!copy(num); brackets are optional for invocation-args if it can have just the first argument; a constructor-name of!is an alias for!copy– ex:num!equalsInteger!copy(num); and ifexpr!identdoes not match a constructor it will tryExprClass!copy(expr).ident– ex:str!uppercaseequalsString!copy(str).uppercase. ↩ -
Akin to
expr.invoke(...)orexpr._invoke(...)depending if expression immediate or durational – and if enough context is available the arguments are compile-time type-checked plus adding any default arguments. ↩ -
Gets item (or sets item if binding present) at specified index object. Syntactic sugar for
at()orat_set(). ↩ -
If receiver ‘List’ type - each item sent call – coroutines called using
%–sync,%>–raceconcurrency depending on delimiter. If receiver empty list ornilinvocation is skipped. If not list ornilthen executes like normal invoke call – i.e. ‘%’ is synonymous to ‘.’. ↩ -
Every operator has a named equivalent. For example
:=andassign(). Operators do *not* have a special order of precedence – any order other than left to right must be indicated by using code block brackets (‘[’ and ‘]’). ↩ -
In order to be recognized as single subtract ‘
-’ expression and not an expression followed by a second expression that starts with a minus sign, the minus symbol ‘-’ must either have whitespace following it or no whitespace on either side. ↩ -
See math-operator footnote about subtract on how to differentiate from a negation ‘
-’ prefix operator. ↩ -
bracketed-args may be omitted if the invocation can have zero arguments. ↩
-
Routines with last send parameter as mandatory closure may omit brackets ‘
()’ and closure arguments may be simple code-block (omitting ‘^’ and parameters and inferring from argument parameter context). Default arguments indicated via comma ‘,’ separators. ↩ -
If a temporary is defined in the return-arg, it has scope for the entire surrounding code block. ↩
-
Named arguments may only be used at end of the argument list – and once used may only be followed by other named arguments. Named arguments may also specify an argument for a group-param, but must be compatible
Listobject. Named arguments evaluated in parameter index order regardless of order they appear since defaults may reference earlier parameters. Note that bind may not be used as an argument. ↩ -
Compiler *hint* that expression evaluates to specified class – otherwise error. class-desc optional if desired type can be inferred. If expression is variable-identifier then parser updates type context. [Debug: runtime ensures class specified is received.] ↩
-
Explicit conversion to specified class. class-name optional if desired type inferable. Ex:
42>>Stringcalls conversion methodInteger@String()i.e.42.String()- whereas"hello">>Stringgenerates no extra code and is equivalent to"hello". ↩ -
expr1??expr2is essentially equivalent toif expr1.nil? [expr2] else [expr1<>TypeNoneRemoved]. ↩ -
The optional instance-name identifies the loop for specific reference by a loop-exit which is useful for nested loops. ↩
-
Only valid in the code block scope of the loop that it references. ↩
-
2+ durational expressions run concurrently and next expression executed when *all* expressions returned (result
nil, return args bound in order of expression completion). ↩ -
2+ durational expressions run concurrently and next expression executed when *fastest* expression returns (result
nil, return args of fastest expression bound) and other expressions are *aborted*. ↩ -
Durational expression run concurrently with surrounding context and the next expression executed immediately (result
InvokedCoroutine). expression is essentially a closure with captured temporary variables to ensure temporal scope safety. Any return arguments will be bound to the captured variables. ↩ -
Rather than inheriting the caller’s updater
Mindobject, durational expressions in the second expression are updated by the mind object specified by the first expression. ↩ -
If optional ‘
?’ is used in query/predicate method name, use ‘-Q’ as a substitute since question mark not valid in a file name. ↩ -
Only immediate calls are permissible in the code block. If code-block is absent, it is defined in C++. ↩
-
If code-block is absent, it is defined in C++. ↩
-
A file name appended with ‘
C’ indicates that the file describes class members rather than instance members. ↩ -
Optional class-desc is compiler hint for expected type of member variable. If class omitted,
Objectinferred orBooleanif data-name ends with ‘?’. If data-name ends with ‘?’ and class-desc is specified it must beBoolean. ↩ -
The context / file where an annotation is placed limits which values are valid. ↩
-
Optional class-desc is return class – if type not specified
Objectis inferred (orBooleantype for predicates orAuto_type for closures) for nested parameters / code blocks andInvokedCoroutineis inferred for coroutine parameters. ↩ -
The optional binding indicates the parameter has a default argument (i.e. supplied expression) when argument is omitted. ↩
-
If optional class-desc is omitted
Objectis inferred orAuto_for closures orBooleanif variable-name is predicate and ends with ‘?’. If variable-name ends with ‘?’ and class-desc is specified it must beBoolean. ↩ -
Objectinferred if no classes specified. Class of resulting list bound to instance-name is class union of all classes specified. ↩ -
Indicates that the class is any one of the classes specified and which in particular is not known at compile time. ↩
-
‘
_’ indicates durational (like coroutine), ‘+’ indicates durational or immediate (coroutine or method) and lack of either indicates immediate (like method). Class ‘Closure’ matches any closure interface. Identifiers and defaults used for closure arguments without parameters. ↩ -
Listcan be any class derived fromList. If class-desc in item class descriptor is omitted,Objectis inferred when used as a type or the item type is deduced when used with a *list-literal. A list-class of any item type can be passed to a simple untypedListclass. ↩ -
Special escape characters: ‘
n’ - newline, ‘t’ - tab, ‘v’ - vertical tab, ‘b’ - backspace, ‘r’ - carriage return, ‘f’ - formfeed, and ‘a’ - alert. All other characters resolve to the same character including ‘\’, ‘"’, and ‘'’. ↩