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

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’ | ‘esimple-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 = elsews code-block
when = expression wswhenws expression
unless = expression wsunlessws expression
case = casews 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 = syncws code-block
race34 = racews code-block
branch35 = branchws expression
change-mind36 = changews 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.

  1. Here is an example footnote. 

  2. r’ indicates (r)adix / base – default 10 (decimal) if omitted. Ex: 2r binary & 16r hex. big-digit(s) vary by the radix used. 

  3. Negative simple-integer can be confused with subtract math-operator

  4. Decimal part of real-literal is optional if Real type can be inferred from context or if the exponent part is present. 

  5. 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 then Object used as default item type). 

  6. [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 provided this 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 surrounding this or temporary/parameter variables are used and captured may all be inferred if omitted. 

  7. Note that scoping is not necessary – instance names may not be overridden and classes and implicit identifiers effectively have global scope. 

  8. Optional expression can be used to access data member from an object – if omitted, this is inferred. 

  9. @’ indicates instance data member and ‘@@’ indicates class instance data member. 

  10. If class-name absent Actor inferred or desired type if known. If optional ‘?’ is present and object not found at runtime then result is nil else assertion error occurs. Optional hash mark ‘#’ indicates no lookup - just return name identifier validated by class type. 

  11. A method using class-name allows explicit conversion similar to class-conversion except that the method is always called. 

  12. Optional ‘?’ used as convention to indicate predicate variable or method of return type Boolean (true or false). 

  13. Destructor calls are only valid in the scope of another destructor’s code-block

  14. Only valid in the scope of a code-block

  15. May not be used as an argument. Only a named-spec is valid as an argument which looks similar. 

  16. If an invoke-call’s optional expression (the receiver) is omitted, ‘this.’ is implicitly inferred. 

  17. 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 for ExprClass!ctor(expr) – ex: num!copy equals Integer!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! equals Integer!copy(num); and if expr!ident does not match a constructor it will try ExprClass!copy(expr).ident – ex: str!uppercase equals String!copy(str).uppercase

  18. Akin to expr.invoke(...) or expr._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. 

  19. Gets item (or sets item if binding present) at specified index object. Syntactic sugar for at() or at_set()

  20. If receiver ‘List’ type - each item sent call – coroutines called using %sync, %>race concurrency depending on delimiter. If receiver empty list or nil invocation is skipped. If not list or nil then executes like normal invoke call – i.e. ‘%’ is synonymous to ‘.’. 

  21. Every operator has a named equivalent. For example := and assign(). 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 ‘]’). 

  22. 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. 

  23. See math-operator footnote about subtract on how to differentiate from a negation ‘-’ prefix operator. 

  24. bracketed-args may be omitted if the invocation can have zero arguments. 

  25. 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. 

  26. If a temporary is defined in the return-arg, it has scope for the entire surrounding code block. 

  27. 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. 

  28. 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.] 

  29. Explicit conversion to specified class. class-name optional if desired type inferable. Ex: 42>>String calls conversion method Integer@String() i.e. 42.String() - whereas "hello">>String generates no extra code and is equivalent to "hello"

  30. expr1??expr2 is essentially equivalent to if expr1.nil? [expr2] else [expr1<>TypeNoneRemoved]

  31. The optional instance-name identifies the loop for specific reference by a loop-exit which is useful for nested loops. 

  32. Only valid in the code block scope of the loop that it references. 

  33. 2+ durational expressions run concurrently and next expression executed when *all* expressions returned (result nil, return args bound in order of expression completion). 

  34. 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*

  35. 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. 

  36. 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. 

  37. If optional ‘?’ is used in query/predicate method name, use ‘-Q’ as a substitute since question mark not valid in a file name. 

  38. Only immediate calls are permissible in the code block. If code-block is absent, it is defined in C++. 

  39. If code-block is absent, it is defined in C++. 

  40. A file name appended with ‘C’ indicates that the file describes class members rather than instance members. 

  41. Optional class-desc is compiler hint for expected type of member variable. If class omitted, Object inferred or Boolean if data-name ends with ‘?’. If data-name ends with ‘?’ and class-desc is specified it must be Boolean

  42. The context / file where an annotation is placed limits which values are valid. 

  43. Optional class-desc is return class – if type not specified Object is inferred (or Boolean type for predicates or Auto_ type for closures) for nested parameters / code blocks and InvokedCoroutine is inferred for coroutine parameters. 

  44. The optional binding indicates the parameter has a default argument (i.e. supplied expression) when argument is omitted. 

  45. If optional class-desc is omitted Object is inferred or Auto_ for closures or Boolean if variable-name is predicate and ends with ‘?’. If variable-name ends with ‘?’ and class-desc is specified it must be Boolean

  46. Object inferred if no classes specified. Class of resulting list bound to instance-name is class union of all classes specified. 

  47. Indicates that the class is any one of the classes specified and which in particular is not known at compile time. 

  48. _’ 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. 

  49. List can be any class derived from List. 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 untyped List class. 

  50. wsr is an abbreviation for (w)hite (s)pace (r)equired. 

  51. 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 ‘'’.