Droid2Grammar

This is the context free grammar for Droid , expressed in Extended Backus–Naur Form (EBNF ).

Quoted strings match the contained literal text, as do words. <x> matches if the rule of the the corresponding name matches. <x> ::= defines a non-terminal (rule) to whatever comes after the ::=. Parenthesis group, pipes denote alternatives, brackets denote an optional sub-rule and curly braces mean that the contained rule must occur zero or more times in succession. Text contained between a pair of question marks are informal sub-rules.

Top Level Constructs

<module> ::= [ ";" ] [ package <lower> { "." <lower> } [ ";" ] ] 
             { using [ <lower> { "." <lower> } "." ] <upper> { "," <upper> } [ ";" ] }
             { ( <interface> | <enum> | <class> | <object> ) [ ";" ] }

<interface> ::= <doc> [ using ] interface <upper> <generic> <implement> <where>
                do <method-heads> end

<enum> ::= <doc> [ using ] enum <upper> <generic> <implement> <where>
           do <upper> [ <parameters> ] { [ ";" ] <upper> [ <parameters> ] } 
           [ methods <methods> ] end

<class> ::= <doc> [ using ] class <upper> <generic> <parameters> <implement> <where>
            [ do <exp-block> [ methods <methods> ] end ]

<object> ::= <doc> [ using ] object <upper> <implement> <where>
             do <exp-block> [ methods <methods> ] end

Expressions

<exp> ::= "(" <exp> ")" | <variable> | <literal>
        | <field> | <binop> | <unop> | <call>
        | <scope> | <while> | <for> | <if> | <switch>
        | <var> | <val> | <function> | <lambda>

<block> ::= do <exp-block> end
<scope> ::= scope <block>
<while> ::= while <exp> <block>
<for> ::= for <lower> [ ":" <type> ] in <exp> { "," <lower> [ ":" <type> ] in <exp> } [ where <exp> ] <block>
<if> ::= if <exp> do <exp-block> { elseif <exp> do <exp-block> } [ else <exp-block> ] end
<switch> ::= switch <exp> do <match-block> end

<var> ::= var <lower> [ ":" <type> ] "=" <exp>
<val> ::= val <lower> [ ":" <type> ] "=" <exp>
<lambda> ::= <lambda-head> <block>
<lambda-head> ::= fun "(" [ <lower> [ ":" <type> ] { "," <lower> [ ":" <type> ] } ] [ "," ] ")" [ ":" <type> ] <where>
<function> ::= <function-head> <block>
<function-head> ::= fun <lower> <parameters> ":" <type> <where>

<call> ::= <exp> "(" [ <exp> { "," <exp> } ] [ <lower> ":" <exp> { "," <lower> ":" <exp> } ] [ "," ] ")"

Miscellaneous Definitions

<methods> ::=  { [ ";" ] <doc> <function> } [ ";" ]

<method-heads> ::= { [ ";" ] <doc> <function-head> } [ ";" ]

<doc> ::= [ <doc-comment> [ ";" ] ]

<literal> ::= <simple-literal> | <container-literal>

<container-literal> ::= "[" [ <exp> { "," <exp> } [ "," ] ] "]"

<simple-literal> ::= <integer> | <float> | <string>

<variable> ::= <lower> | <upper>

<field> ::= <exp> "." ( <lower> | <upper> )

<exp-block> ::= [ ";" ] [ <exp> { ";" <exp> } [ ";" ] ] [ throw <exp> [ ";" ] ]

<catch> ::= [ catch <match-block> ]

<parameters> ::= "(" [ <lower> ":" <type> { "," <lower> ":" <type> } ] [ "," ] ")"

<match-block> ::= [ ";" ] { <pattern> <block> [ ";" <matches> ] } [ ";" ] <catch>

<pattern> ::= "(" <pattern> ")"
            | <lower> [ ":" <type> ]
            | <upper> [ "(" <pattern> { "," <pattern> } [ "," ] ")" ]
            | "[" [ <pattern> { "," <pattern> } ] [ "," ] "]" [ "@" <lower> ]
            | <simple-literal>

<type> ::= <upper> [ "[" [ <type> { "," <type> } [ "," ] ] "]" ]
         | "{" <type> { "," <type> } "}" | <lambda-head>
         | <type> "*" | <type> "?"

<where> ::= [ where <type> ":" <type> { "," <type> ":" <type> } [ "," ] ]

<implement> ::= [ ":" <type> { "," <type> } ]

<generic> ::= [ "[" <upper> { "," <upper> } "]" ]

Informal Definitions

These definitions are here to help implementing the lexer.

<binop> ::= ? Binary operators (infix): .. @ to in + - * / ^ and or = == != < > <= >= ?
<unop> ::= ? Unary operators (prefix): scope not - ?

<lower> ::= ? Lower-case identifier [a-z][A-Za-z0-9]* ? 
<upper> ::= ? Upper-case identifier [A-Z][A-Za-z0-9]* ? 

<integer> ::= ? C-like base 10 and base 16 integers ?
<float> ::= ? C-like double, but there must be at least one digit before the dot ?
<string> ::= ? C-like double quoted strings, plus the same just for single quotes ?

<line-break> ::= ? Any of \r or \n or \r\n (semicolon inserted in some cases) ?
<doc-comment> ::= ? The contents of all the ## comments in a row ?
<comment> ::= ? Anything (else) after a # on a line, ignored as whitespace ?
<white-space> ::= ? Anything with an ASCII code less than or equal to 32, ignored ?

The Lexer

The lexer should follow the informal definitions and recognize the keywords and tokens found in the above definitions. It should insert a semicolon when reaching the end of a line, if the last token was not { or [ or ( or an infix or prefix operator. Multiple semicolons in a row will be reduced to one in the output token stream.

Code Samples

See more samples .

## This is a doc-comment that
## spans multiple lines.
class Vector2[T](x: T, y: T) where T: Number do
    val magnitudeSquared = x ^ 2 + y ^ 2
methods
    fun getMagnitudeSquared(): T do magnitudeSquared end
    fun getX(): T do x end
    fun getY(): T do y end
    fun add(other: Point[T]): Point[T] do 
        val a = x + other.X
        val b = y + other.Y
        Point(a, b) 
    end
end

Misc.

There could be conjunctive (must-match-both) and disjunctive (must-match-at-least-one) patterns:

switch x do
    [x, y] @ _ and l do ... end
    [0, x] or [x, 0] do ... end
end

Instead of creating manual getters and setters, sticking val in front of a constructor variable could make an implicit getter for it, and var could make both an implicit getter and setter. User-defined getters and setters would still take precedence:

class Point(val x: Int, var y: Int)
# is equivalent to
class Point(x: Int, y: Int) do
    var y = y
methods
    fun getX(): Int do x end
    fun getY(): Int do y end
    fun setY(value: Int): Void do y = value end
end

There is little need for a setter-only shortcut, since those are very rare, and almost definitely require special behavior anyways.

There could be a shorthand for immutable POD types.

fun getPosition(): (X: Int, Y: Int) do
    (X: x, Y: y)
end

Which would be equivalent to

using class AnonymousClass(val x: Int, val y: Int)
...
fun getPosition(): { fun getX(): Int, fun getY(): Int } do
    AnonymousClass(x: x, y: y)
end

Appropriate deconstructors would be nice:

val position = foo.getPosition()
val (X: x, Y: y) = position
switch position do
    (X: x, Y: y) do ... end
end