SubScript is able to describe its own syntax concisely:
script..
subScriptCode = . "override" & . "implicit"; "script";
scriptDefinition
+ ".." (scriptDefinition..)
scriptDefinition = scriptHeader . ("+=" + "=") scriptExpression
scriptHeader = scriptName_dots (.formalParameterList) (.":" typer) .. ","
|+| channelName_dots (.formalParameterList) (.":" typer)
scriptName_dots = scriptName . ".." + ("."+"..") scriptName
channelName_dots = (.identifier) doubleArrow_dots
scriptName = identifier %; simpleArrow
formalParameterList = "(" (.formalParameters) ")"
actualParameterList = "(" (.actualParameters) ")"
identifiers = identifier .. ","
formalParameters = formalParameter .. ","
actualParameters = actualParameter .. parameterSeparator
simpleActualParameters = simpleActualParameter .. parameterSeparator
formalParameter = . formalOutputMarker; identifier ":" typer
actualParameter = valueExpression
+ actualOutputMarker valueExpression
(.postCondition) (. ":" type)
simpleActualParameter = simpleValueExpression
+ actualOutputMarker simpleValueExpression
(.postCondition) (. ":" type)
formalOutputMarker = "?" + "??"
actualOutputMarker = "?" + "??"
postCondition = "?if" valueExpression
scriptExpression = operatorModifiers dataflowExpression_HP
dataflowExpression_HP = scriptExpression_9 .
( "~~~>" scriptLambdaTerm_HP
. "+~/~~>" scriptLambdaTerm_HP )
+ ( "~/~~>" scriptLambdaTerm_HP )
+ ( "~~~" "(" dataflowParameter ")" "~~>" scriptLambdaTerm_HP
.. "+~~~" "(" dataflowParameter ")" "~~>" scriptLambdaTerm_HP
.. "+~/~~" "(" dataflowParameter ")" "~~>" scriptLambdaTerm_HP )
+ ( "~/~~" "(" dataflowParameter ")" "~~>" scriptLambdaTerm_HP
.. "+~/~~" "(" dataflowParameter ")" "~~>" scriptLambdaTerm_HP )
dataflowParameter = pattern . "if" valueExpression
operatorModifiers = . ("," + naryOperatorDesignator) . naryOperatorDesignator
scriptExpression_9 = scriptExpression_8 .. newLine
scriptExpression_8 = scriptExpression_7 .. (+ ";" ";-;")
scriptExpression_7 = "if" valueExpression "then" scriptExpression_8
. "else" scriptExpression_8
+ "do" scriptExpression_7 ( "then" scriptExpression_8
%; "else" scriptExpression_8 )
+ scriptExpression_6
scriptExpression_6 = scriptExpression_5 .. (+ "||" "|"
orParArrow
"|+" "|;" "|/"
"||+" "||;" "||/"
"|+|" "|;|" "|/|")
scriptExpression_5 = scriptExpression_4 .. (+ "&&" "&" andParArrow)
scriptExpression_4 = scriptExpression_3 .. "=="
scriptExpression_3 = scriptExpression_2 .. "+"
scriptExpression_2 = scriptExpression_1 .. (+ "/" "%" "%/" "%/%/" "%&" "%;")
scriptExpression_1 = scriptSpaceExpression .. "·"
scriptSpaceExpression = scriptDataFlow .. if commasOmittable (-) else (+)
scriptDataFlow = scriptTerm (..; "~~>;" scriptLambda %; "~/~>;" scriptLambda)
scriptLambda = . parameter "==>;"; simpleScriptTerm
scriptTerm = unary
+ variableDeclaration
+ valueDeclaration
+ privateDeclaration
unary = ..(unaryPrefixOperator + directive); simpleScriptTerm
directive = "@" scalaCode ":"
unaryPrefixOperator =+ "!" "-" "~"
simpleScriptTerm =; |+|
scriptCall
codeFragment
matchTerm
throwTerm
whileTerm
forTerm
tryTerm
specialTerm
"(" scriptExpression ")"
"(*" scriptExpression "*)"
"(**" scriptExpression "**)"
scriptCall = implicitScriptCall
|+| methodOrScriptCall
|+| channelScriptCall; . resultHandler
implicitScriptCall = simpleActualParameters .postCondition
methodOrScriptCall = simpleValueExpression . actualParameterList .postCondition
|+| simpleValueExpression "," simpleActualParameters
channelScriptCall = .simpleValueExpression;
identifier_arrow; (+) + actualParameterList .postCondition
+ simpleActualParameters
resultHandler = "^" + "^^" "{" scalaCode "}"
parameterSeparator = "," + if commasOmittable (+)
specialTerm =+ "(-)" "(+)" "(+-)" "." ".." "..." "break"
valueDeclaration = "val" identifier; . ":" typer ; "=" (.directive) simpleValueExpression
variableDeclaration = "var" identifier; ":" typer %; "=" (.directive) simpleValueExpression
privateDeclaration = "private" identifiers
codeFragment =; +
"{" scalaCode "}"
"{*" scalaCode "*}"
"{?" scalaCode "?}"
"{!" scalaCode "!}"
"{^" scalaCode "^}"
"{." scalaCode ".}"
"{..." scalaCode "...}"
whileTerm = "while" valueExpression
throwTerm = "throw" valueExpression
forTerm = "for"; "(" enumerators ")" + "{" enumerators "}"
tryTerm = "try" unary (scriptCatchClause %; scriptFinallyClause)
matchTerm = simpleValueExpression "match" "{" scriptCaseClauses "}"
scriptCatchClause = "catch" "(" (scriptCaseClause..) ")"
scriptCaseClause = "case" pattern (. "if" valueExpression)
("=>;" + "*=>;") scriptExpression
scriptFinallyClause = "finally" "{" scalaCode "}"
valueExpression = parenthesizedExpression + simpleValueExpression
parenthesizedExpression = "(" scalaExpression ")"
naryOperatorDesignator =+ ";" "-;"
"||" "|"
orParArrow
"|+" "|;" "|/"
"||+" "||;" "||/"
"|+|" "|;|" "|/|"
"&&" "&"
andParArrow
"=="
"+"
"/" "%" "%/" "%/%/" "%&" "%;"
"·"
("?" simpleValueExpression ":")
andParArrow = "&~~>;" + "&~~{" scalaCode "}~>;" + "&~~(" scalaTupel ")~~>;"
+ "&&~~>;" + "&&~~{" scalaCode "}~>;" + "&&~~(" scalaTupel ")~~>;"
orParArrow = "|~~>;" + "|~~{" scalaCode "}~>;" + "|~~(" scalaTupel ")~~>;"
+ "||~~>;" + "||~~{" scalaCode "}~>;" + "||~~(" scalaTupel ")~~>;"
channelName_dots =+ "<-->;" "<~~>;"
"<-.->;" "<~.~>;"
"<-..->;" "<~..~>;"
"<-->;.." "<~~>;.."
simpleArrow =+ "<-" "<~" "->;" "~>;"
arrow =+ simpleArrow "<-*" "<~*" "*->;" "*~>;" "?->;" "?~>;"
simpleValueExpression = "_"
+ literal
+ "new" (classTemplate + templateBody)
+ ( "here"
+ currentInstanceExpression
+ identifier . "." currentInstanceExpression )
(.. "." identifier)
currentInstanceExpression = "this" + "super" "." identifier
Notes
x %; y means: at least one of x and y, and x comes before y.
commasOmittable is a stacked variable set at the start of each scriptExpression, based on the parsing of operatorModifiers:
commasOmittable = there is at least one operator modifier and the first one is a comma
If there is at least one operator modifier, then the first one sets the meaning of the space (this may even get the meaning of a comma) in scriptSpaceExpression
If there are two operator modifiers, then the second one sets the meaning of the newline operator in scriptExpression9.
newLine is a pseudo token that denotes a that the next token is on a different line.
Special symbols
References to Scala syntax:
scalaCode classTemplate literal type enumerators
scalaExpression templateBody identifier pattern
identifier_arrow is a concatenation of: an identifier or an empty string, and an arrow, e.g., a*<= and ?->.
· is a math multiplication symbol denoting sequence, with strong binding.
Script Definitions
Script definitions have ambiguous syntax. Spacing determines where script definitions end.
E.g.,
script a = b c d
c belongs to script a, since its starting column is to the right of the starting column of the script definition line.
d is outside the script.
script ..
a = b
c = d
e
f = g
h = i
j
a, c, f and h are script definitions.
e belongs to c since its starting column is at least the column of the equals symbol of script c.
j is outside the script.. section.
Other Ambiguities
As in many programming languages, there is a syntactic ambiguity with nested if-else constructs.
In such cases an ambiguous else belongs to the immediate preceding if.
For implicit script calls, an optional postcondition belongs to the parameter list.
E.g. suppose there is an implicit script key(??c:Char)
and there is a var c:Char in scope.
Then in ?c ?if isDigit the postcondition refers to the output parameter c.
There would be no alternative here to enforce that the postcondition is related to the implicit script, instead of the parameter.
Script expressions will often have white space as implicit n-ary operator.
Such whitespace may be omitted around (+-) (-) (+) .. ... and after ) and . (optional break).
There is still ambiguity with a.b and a(b).
If there is white space before . it is an optional break; else it is a path separator.
If there is white space before ( it starts a parenthesized expression; else it starts a parameter list
LL(1) grammar
The SubScript syntax contains ambiguous choices between method calls, script calls and match terms.
Note that simpleTerm and methodOrScriptCall use for this purpose the disambiguating choice operator |+|.
This is an experimental SubScript feature under development.
The ambiguous choice keeps the syntax definition well readable for humans, but it is not a good starting point to write a top-down parser in a language such as Scala. Such an LL parser looks only one token ahead; it is specified using an LL(1) grammar.
An LL(1) grammar for SubScript would have a slightly different definition for simpleTerm, and instead of implicitScriptCall, methodOrScriptCall, channelScriptCall and matchTerm there would be two rather complicated scripts:
simpleTerm =; +
simpleValueLedTerm
codeFragment
throwTerm
whileTerm
forTerm
tryTerm
specialTerm
&quot;(&quot; scriptExpression &quot;)&quot;
identifier_arrowLedTerm = identifier_arrow
. (actualParameterList + simpleActualParameters)
simpleValueLedTerm = identifier_arrowLedTerm
+ simpleValueExpression;
&quot;match&quot; &quot;(&quot; scriptCaseClauses &quot;)&quot;
+ ( &quot;.&quot; identifier_arrowLedTerm
+ (.. parameterSeparator simpleActualParameter)
+ actualParameterList
)
Indexed Script Expressions
Another preparation for an implementation in Scala would be to replace scriptExpression_0 by a script ..scriptExpression_8scriptExpression(i:Int) that has an index as a parameter:
scriptExpression = operatorModifiers scriptExpression_8
scriptExpression(i:Int) = if (i&gt;=0) (scriptExpression(i-1) .. operator(i))
else scriptTerm
operator(i:Int) = i matches (
case 8 ==&gt; if newLineSignificant newLine else δ
case 7 ==&gt; &quot;;&quot; &quot;-;&quot;
case 6 ==&gt; + &quot;||&quot; &quot;|&quot; &quot;||:&quot; &quot;|:&quot;
&quot;|+&quot; &quot;|;&quot; &quot;|/&quot;
&quot;||+&quot; &quot;||;&quot; &quot;||/&quot;
&quot;|+|&quot; &quot;|;|&quot; &quot;|/|&quot;
case 5 ==&gt; + &quot;&amp;&amp;&quot; &quot;&amp;&quot; &quot;&amp;&amp;:&quot; &quot;&amp;:&quot;
case 4 ==&gt; + &quot;==&quot; &quot;==:&quot; networkingArrow
case 3 ==&gt; &quot;+&quot;
case 2 ==&gt; + &quot;/&quot; &quot;%&quot; &quot;%/&quot; &quot;%/%/&quot; &quot;%&amp;&quot; &quot;%;&quot;
case 1 ==&gt; + &quot;·&quot;
case 0 ==&gt; if commasOmittable (-) else (+)
)