// BNF syntax that is a subset of SLK, http://slkpg.byethost7.com/documentation.html#SLKsyntax // // Only the colon (:) LHS separator is supported, EBNF features are not supported (e.g. [ ], {} and {}+), // tokens in single quotes are always terminals, and unquoted terminals must be uppercase. // An optional rule must have the first production _epsilon_. // Only comments that begin with "//" are supported. // // The grammar must be context-free with the following special terminals removed from the grammar. // The following special terminals are not syntax errors when they appear in the input and grammar does not expect them. // They are syntax errors when they do not appear in the input and grammar expects them. // '' means no space between the prior (or start of input) and the next token (or end of input). // '\n' means a new line // The following special terminal is never sent by the lexer. It is popped off the parse stack immediately. // '\N' toggle the state of whether '\n' is not syntax error when grammar does not expect it. sources: source sources '\0' source: 'import' ID dotId private oop dotId: _epsilon_ '.' ID dotId private: _epsilon_ 'private' oop: interface mixin class object // General expression, includes 'ifelse', 'noblock', and matching. Does not have an anonymous function expression. exprNF: ifelse noblock matches // General expression, includes 'ifelse', 'noblock', matching, and an anonymous function expression. expr: ifelse noblock matchOrFunc fParamsNotOneID fBody // see 'function' for explanation matchOrFunc: matches guard fBody matches // see 'function' for explanation for 'noblock guard fBody matches' matches: _epsilon_ '|' match matches match: noblock guard fBody guard: // see section “15.3 Pattern guards” of the book “Programming in Scala, First Edition, Version 6”. _epsilon_ 'and' noblock // Expression without a 'block', includes 'noblock', matching, and an anonymous function expression. exprNB: noblock matchOrFuncNB fParamsNotOneID bodyNB // see 'function' for explanation, noting that 'bodyNB' is a subset of 'fBody'. matchOrFuncNB: matchesNB guard bodyNB matchesNB // see 'function' for explanation for 'noblock guard bodyNB matchesNB' // noting that 'bodyNB' is a subset of 'fBody'. matchesNB: _epsilon_ '|' matchNB matchesNB matchNB: noblock guard bodyNB // Anonymous function expression function: noblock guard fBody matches // Either a function that takes one ID parameter, or the pattern match shortened-form for a function that takes one parameter: http://copute.com/dev/docs/Copute/ref/scala.html#match fParamsNotOneID fBody // ...all other cases of function // // Note that 'noblock guard fBody matches' will compile to a function // with one input parameter when 'noblock' is an ID, // and not as "catch remaining" case variable. // Which is correct, since there would be no cases before it. // See the subsection “Variable patterns” within the section “15.3 Kinds of patterns” // of the book “Programming in Scala, First Edition, Version 6”. // // A function with one input parameter that contains a 'bodyNB', // is allowed to contain a non-parenthesized matching, // because of the 'matches' that follows 'noblock guard fBody'. // Functions that input more than one input parameter and contain a 'bodyNB', // are not allowed to contain a non-parenthesized matching. // It is possible to change the grammar to swap this ability between those two types of functions, // (copy 'fBody' to 'mBody', every 'fBody' that precedes a '|' is changed to 'mBody', change 'fBody' to allow matching in its copy of 'bodyNB') // but it is not possible to offer non-parenthesized matching for both because of the next paragraph. // It is possible to make both non-parenthesized by removing the shortened-form. // // Note that 'noblock' allows more general expressions that is allowed for case of matching, // which will thus be a post-parser compiler error. // It is not possible to fork that in an LL(k) grammar, // because the complication is the 'matchOrFunc' fork, // which requires a complete 'noblock' to precede 'matches'. // // It is a post-parser compiler error if there is a 'guard' or 'matches' // when 'noblock guard fBody matches' is a function, i.e. when 'noblock' is only an ID. fParamsNotOneID: ID fParam fParams // At least two function parameters, if first is ID '_' fParams '()' // Function has no input parameters. fParams: _epsilon_ fParam fParams fParam: ID '_' // Parameter isn't used in the function body. fBody: bodyNB block bodyNB: '\N' '->' stmtsNB noblock '\N' // Note an parenthesized 'exprNB' can appear in a 'noblock', in order output a matching or anonymous function expression. block: '{' stmts expr '}' // statements may have an imperative execution order, if the expressions are order-dependent stmtsNB: _epsilon_ stmtNB ';' stmtsNB stmtNB: 'val' idOrTuple '=' exprNB // 'expr' not CFG if remove '\n': http://copute.com/dev/docs/Copute/ref/scala.html#body stmts: _epsilon_ stmt '\n' stmts stmt: 'val' idOrTuple stmt1 'returnif' noblock block // an imperative short-circuit ID '=' exprNF // re-assignment of a local val does not violate pure function idOrTuple: ID '(' idOrBlank ',' idOrBlank idOrBlanks ')' // deconstruct a tuple idOrBlank: ID '_' idOrBlanks: _epsilon_ ',' idOrBlank idOrBlanks stmt1: '=' exprNF ':' stmt2 stmt2: notfType stmt3 fType2 fAnnotate function stmt3: '=' exprNF nextType nextTypes fAnnotate function fAnnotate: '=' '@=' // compiles to '@tailrec' in Scala, http://stackoverflow.com/q/4785502 // Conditional expressions ifelse: 'if' noblock block elseif 'else' block elseif: _epsilon_ 'elseif' noblock block noblock: infix10 cast ternary ternary: _epsilon_ '?' infix10 ':' infix10 ternary // ": infix10 ternary" for right-associative, http://stackoverflow.com/q/7407330. "? infix10" to not conflict with ':' in cast, http://copute.com/dev/docs/Copute/ref/scala.html#body, and also to force "infix10 ternary" to inside of parenthesis to become "infix10". cast: _epsilon_ ':' cType // When ID : cType is the case of a match to a disjunction, translate this to cType(ID), http://stackoverflow.com/q/7460312 // Operator expressions infix10: infix9 lf10 lf10: _epsilon_ '||' infix9 lf10 infix9: infix8 lf9 lf9: _epsilon_ '&&' infix8 lf9 infix8: infix7 lf8 lf8: _epsilon_ lop8 infix7 lf8 lop8: '==' '!=' infix7: infix6 lf7 lf7: _epsilon_ lop7 infix6 lf7 lop7: '<' '<=' '>' '>=' infix6: infix5 lf6 lf6: _epsilon_ lop6 infix5 lf6 lop6: '+' '-' infix5: infix4 lf5 lf5: _epsilon_ lop5 infix4 lf5 lop5: '*' '/' '%' infix4: prefix3 lf4 rt4 lf4: _epsilon_ LOP4 prefix3 lf4 // lexer: http://copute.com/dev/docs/Copute/ref/scala.html#operators rt4: _epsilon_ ROP4 infix4 // lexer: http://copute.com/dev/docs/Copute/ref/scala.html#operators prefix3: instance UNARY prefix3 // lexer: http://copute.com/dev/docs/Copute/ref/scala.html#line // Instance and function call expressions instance: literal postfix2 postfix2: infix1 calls calls: _epsilon_ call calls infix1: primary members members: _epsilon_ '.' ID members // ID.ID can also be for package resolution, when the last ID is a class constructor call primary: ID tuple literal: INTEGER FLOAT STRING tuple: '(' exprNB exprNBs ')' // Sequence of objects of different types. A tuple with a single element, is an operator precedence grouping, i.e. see Any.copu in the standard library for more explanation. exprNBs: _epsilon_ ',' exprNB exprNBs call: '' '(' fArg fArgs ')' // Eliminating the intervening whitespace; this gives consistency to layout, and '' '()' // if necessary to eliminate an ambiguity in the grammar, http://copute.com/dev/docs/Copute/ref/scala.html#line fArgs: _epsilon_ ',' fArg fArgs fArg: exprNB '_' // Curry the function on this argument // Subtyping interface: unsafe sealed 'interface' ID '' tParamList inherits iBody // 'sealed' means no subtype outside the current file is allowed to inherit this interface directly. Any non-sealed subtypes in the current file are allowed to have subtypes though. mixin: 'mixin' ID '' tParamList inherits cBody class: 'class' ID '' tParamList constructor inherits cBody object: impure 'object' ID inherits cBody unsafe: _epsilon_ 'unsafe' sealed: _epsilon_ 'sealed' impure: _epsilon_ 'impure' inherits: _epsilon_ ':' cType constCall cTypes constCall: _epsilon_ '' '(' exprNB ')' '' '()' // Distinguish between inheriting the class from its interface, even when its constructor takes no parameters. cTypes: _epsilon_ ',' cType constCall cTypes iBody: _epsilon_ '{' private iMember iMembers '}' iMembers: _epsilon_ private iMember iMembers iMember: id tParamList ':' fType 'static' ID tParamList ':' fType funcOpt staticImpl staticImpl: ID '\N' '.' '\N' ID '=' function id: ID ID operator operator operator: // The method must input 2 parameters for infix, or 1 for unary. SYMBOL lop5 lop6 lop7 funcOpt: _epsilon_ '=' function cBody: _epsilon_ '{' cMember cMembers '}' cMembers: _epsilon_ cMember cMembers cMember: private id tParamList fTypeOpt '=' function private 'static' ID tParamList ':' fType '=' function staticImpl fTypeOpt: _epsilon_ ':' fType tParamList: _epsilon_ '[' tParam tParams ']' tParams: _epsilon_ ',' tParam tParams tParam: variance ID tParamUnboundedList subtypeof supertypeof variance: _epsilon_ // covariant by default (parameter can be used in return values) 'in' // contravariant (parameter can be used in input arguments) 'io' // invariant (parameter can be used in both input arguments and return values) subtypeof: _epsilon_ ':' type supertypeof: _epsilon_ '>' type tParamUnboundedList: _epsilon_ '' '[' tParamUnbounded tParamsUnbounded ']' tParamsUnbounded: _epsilon_ ',' tParamUnbounded tParamsUnbounded tParamUnbounded: ID tParamUnboundedList '_' // Parameter type isn't referenced in the signature nor body of the function, interface, mixin, or class. cType: ID members tArgsOpt // ID.ID is for package resolution. ID is an interface, may also be a mixin in the context of a mixin, class or object, and may also be a class in the context of a class or object. 'constCall' is for a class only. '[' cType ncTypes ']' // tuple ncTypes: ',' cType ncTypesOpt ncTypesOpt: _epsilon_ ncTypes tArgsOpt: _epsilon_ '' '[' tArg tArgs ']' // the '' is not required to obtain CFG (in every case where it appears before '[' or 'tParamList' in this grammar). This is merely to enforce consistency of layout. tArgs: _epsilon_ ',' tArg tArgs tArg: type //ID tArgsOpt we get this already, where type includes cType '_' // Parameter type isn't referenced in the signature nor body of the function, interface, mixin, or class. constructor: // Never optional, use 'object' for singleton that has no parameters private '(' ID ':' type ids ')' // The constructor call and (access to) its parameters can be private. ids: _epsilon_ ',' ID ':' type ids // Type signature type: optfType fType2 optfType: notfType mfType mfType: _epsilon_ nextTypes '->' xType notfType: cType junction 'Unknown' // See Any.copu in the standard library. 'Everything' junction: _epsilon_ '?' // shorthand for disjunction with 'None' '&' cType conjunction // Anonymous compound conjunction type. Copute doesn't support structural typing, i.e. an optional iBody as Scala does. '|' cTypeOrNone cTypeOrNone: cType disjunction // Anonymous disjunction type. http://stackoverflow.com/q/7460312 'None' conjunction: _epsilon_ '&' cType conjunction disjunction: _epsilon_ '|' cType disjunction nextTypes: _epsilon_ xType xType: notfType '(' fType ')' // Function type signature fType: notfType nextTypes '->' xType fType2 fType2: fType3 '()' '->' xType // An empty tuple means undefined and without a value. In this context it means no parameters, i.e. there is no input. fType3: '(' fType ')' nextTypes '->' xType