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:2r
binary &16r
hex. 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
Real
type can be inferred from context or if the exponent part is present. ↩ -
List
type 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 thenObject
used 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 providedthis
is inferred. Optional ‘_
’ indicates it is durational (like a coroutine) – if not present durational/immediate inferred via code-block. Parameter types, return type, scope, whether surroundingthis
or 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,
this
is inferred. ↩ -
’
@
’ indicates instance data member and ‘@@
’ indicates class instance data member. ↩ -
If class-name absent
Actor
inferred or desired type if known. If optional ‘?
’ is present and object not found at runtime then result isnil
else 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
(true
orfalse
). ↩ -
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!copy
equalsInteger!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!ident
does not match a constructor it will tryExprClass!copy(expr).ident
– ex:str!uppercase
equalsString!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
,%>
–race
concurrency depending on delimiter. If receiver empty list ornil
invocation is skipped. If not list ornil
then 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
List
object. 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>>String
calls conversion methodInteger@String()
i.e.42.String()
- whereas"hello">>String
generates no extra code and is equivalent to"hello"
. ↩ -
expr1??expr2
is 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
Mind
object, 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,
Object
inferred orBoolean
if 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
Object
is inferred (orBoolean
type for predicates orAuto_
type for closures) for nested parameters / code blocks andInvokedCoroutine
is 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
Object
is inferred orAuto_
for closures orBoolean
if variable-name is predicate and ends with ‘?
’. If variable-name ends with ‘?
’ and class-desc is specified it must beBoolean
. ↩ -
Object
inferred 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. ↩ -
List
can be any class derived fromList
. If class-desc in item class descriptor is omitted,Object
is 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 untypedList
class. ↩ -
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 ‘'
’. ↩