Skip to content

Instantly share code, notes, and snippets.

@Chubek
Last active October 18, 2025 07:31
Show Gist options
  • Save Chubek/0ab33e40b01a029a7195326e89646ec5 to your computer and use it in GitHub Desktop.
Save Chubek/0ab33e40b01a029a7195326e89646ec5 to your computer and use it in GitHub Desktop.

Revisions

  1. Chubek revised this gist Mar 19, 2024. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -20,4 +20,3 @@ It's pretty simple.

    Thanks bascialyl it.

    PS: In NeoVim, this was pretty formatted, every ::= was under one another. I don't know why Github editor has ruined it?
  2. Chubek created this gist Mar 19, 2024.
    378 changes: 378 additions & 0 deletions ECMAScript.ebnf
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,378 @@
    # Syntactic Grammar for ECMAScript
    ecma-script-module ::= { top-level | ignorable }

    top-level ::= statement
    | function-declaration
    | class-declaration

    function-declaration ::= [ "async" ] "function" identifier function-params-postfix compound-statement

    class-declaration ::= "class" identifier [ "extends" comma-separated-expressions ] class-body

    class-body ::= '{' { class-member } '}'

    class-member ::= [ "static"
    | "getter"
    | "setter"
    | "public"
    | "private" ] ( class-method | class-property )

    class-property ::= [ '#' ] null-join identifier [ '=' expression ] [ ';' ]

    class-method ::= [ '#' ] null-join identifier function-params-postfix compound-statement

    compound-statement ::= '{' { statement } '}'

    statement ::= expression-statement
    | declaration-statement
    | if-statement
    | switch-statement
    | for-statement
    | for-in-statement
    | for-of-statement
    | do-while-statement
    | control-flow-statement
    | try-catch-finally-statement
    | label-statement
    | export-statement
    | import-statement


    export-statement ::= "export" [ "default" ] declaration-statement [ ';' ]
    | "export" module-element "from" module-element [ ';' ]


    import-statement ::= "import" module-element [ ';' ]
    | "import" paren-enclosed-string [ ';' ]
    | "import" module-element "from" module-element [ ';' ]

    label-statement ::= identifier ':' { statement }

    with-statement ::= "with" '(' expression ')' compound-statement

    try-catch-finally-statemnt ::= "try" compound-statement
    [ "catch" [ '(' expression ')' ] compound-statement ]
    [ "finally" compound-statement ]

    control-flow-statement ::= "break" [ ';' ]
    | "continue" [ ';' ]
    | "return" expression [ ';' ]
    | "throw" expression [ ';' ]

    do-while-statement ::= "do" compound-statement "while" '(' expression ')' [ ';' ]

    for-of-stetement ::= "for" '(' expression "of" expression ')' compound-statement

    for-in-statement ::= "for" '(' expression "in" expression ')' compound-statement

    for-statement ::= "for" '(' declaration-statement ';' expression ';' expression ')' compound-statement

    switch-statement ::= "switch" '(' expression ')' { case-clause }

    case-clause ::= "case" expression ':' { statement }
    | "defualt" ':' { statement }

    if-statement ::= "if" '(' expression ')'
    compound-statement [ { "else" if-statement } | "else" compound-statement ]

    declaration-statement ::= ( "var" | "let" | "const" | "static" ) lvalue '=' expression [ ';' ]

    lvalue ::= identifier
    | destructure-target

    destructure-target ::= object-literal
    | array-literal

    expression-statement ::= expression ';'

    expression ::= ternary-expression
    | binary-expression
    | unary-expression
    | primary-expression
    | assignment-expression

    assignment-expression ::= primary-expression '=' expression
    | primary-expression "+=" expression
    | primary-expression "-=" expression
    | primary-expression "*=" expression
    | primary-expression "/=" expression
    | primary-expression "%=" expression
    | primary-expression "**=" expression
    | primary-expression ">>=" expression
    | primary-expression "<<=" expression
    | primary-expression ">>>=" expression
    | primary-expression "&=" expression
    | primary-expression "^=" expression
    | primary-expression "|=" expression
    | primary-expression "&&=" expression
    | primary-expression "||=" expression
    | primary-expression "??=" expression

    ternary-expression ::= binary-expression '?' binary-expression ':' binary-expression

    binary-expression ::= nullish-binary-expression

    nullish-binary-expression ::= logical-or-binary-expression "??" nullish-binary-expression

    logical-or-binary-expression ::= logical-and-binary-expression "||" logical-or-binary-expression

    logical-and-binary-expression ::= bitwise-xor-binary-expression "&&" logical-and-binary-expression

    bitwise-or-binary-expression ::= bitwise-xor-binary-expression '|' bitwise-or-binary-expression

    bitwise-xor-binary-expression ::= bitwise-and-binary-expression '^' bitwise-xor-binary-expression

    bitwise-and-binary-expression ::= equality-binary-expression '&' bitwise-and-binary-expression

    equality-binary-expression ::= relational-binary-expression "==" equality-binary-expression
    | relational-binary-expression "===" equality-binary-expression
    | relational-binary-expression "!=" equality-binary-expression
    | relational-binary-expression "!==" equality-binary-expression

    relational-binary-expression ::= bitwise-shift-binary-expression '<' relational-binary-expression
    | bitwise-shift-binary-expressipn "<=" relational-binary-expression
    | bitwise-shift-binary-expression '>' relational-binary-expression
    | bitwise-shift-binary-expression ">=" relational-binary-exression
    | bitwise-shift-binary-expresssion "??" relational-binary-expression

    bitwise-shift-binary-expression ::= additive-binary-expression ">>" bitwise-shift-binary-expression
    | additive-binary-expression "<<" bitwise-shift-binary-expression
    | additive-binary-expression ">>>" bitwise-shift-binary-expression

    additive-binary-expression ::= multiplicative-binary-expression '+' additive-binary-expression
    | multiplicative-binary-expression '-' additive-binary-expression

    multiplicative-binary-expression ::= unary-expression '*' multiplicative-binary-expression
    | unary-expression '/' multiplicative-binary-expression
    | unary-expression '%' multiplicative-binary-expression
    | unary-expression "**" multiplicative-binary-expression

    unary-expression ::= prefix-expression | postfix-expression

    postfix-expression ::= primary-expression null-join property-access-postifx
    | primary-expression function-call-postfix
    | primary-expression null-join "++"
    | primary-expression null-join "--"
    | primary-expression null-join template-literal
    | primary-expression null-join '?'

    prefix-expression ::= '+' null-join primary-expression
    | '-' null-join primary-expression
    | "++" null-join primary-expression
    | "--" null-join primary-expression
    | '!' null-join primary-expression
    | '~' null-join primary-expression
    | "..." null-join expression
    | "typeof" expression
    | "void" expression
    | "delete" expression
    | "await" exprssion
    | "new" expression

    primary-expression ::= lexical-literal
    | syntactic-literal
    | paren-enclosed-expression
    | class-expression
    | function-expression
    | arrow-expression
    | "this"
    | identifier
    | typcons-name

    arrow-expression ::= [ "async" ] arrow-expression-params "=>" arrow-expression-body

    arrow-expression-body ::= compound-statement-block
    | expression

    arrow-expression-params ::= function-call-expression
    | identifier

    class-expression ::= "class" class-body

    function-expression ::= [ "async" ] "function" function-params-postfix compound-statement

    paren-enclosed-expression ::= '(' [ expression ] ')'

    syntactic-literal ::= template-literal
    | object-literal
    | array-literal


    template-literal ::= '`' { printable | template-expression } '`'

    template-expression ::= "${" [ expression ] '}'

    object-literal ::= '{' [ comma-separated-properties ] '}'

    comma-separated-properties ::= property { ',' property }

    property ::= [ identifier ':' ] expression

    array-literal ::= '[' [ comma-separated-expressions ] ']'

    property-access-postfix ::= bracket-notation-property | dot-notation-property

    bracket-notation-property ::= { bracket-enclosed-expressions }

    bracket-enclosed-expression ::= '[' expression ']'

    dot-notation-property ::= '.' null-join dot-separated-expressions

    dot-separated-expressions ::= expression { '.' null-join expression }

    function-call-postfix ::= '(' [ comma-separated-expressions ] ')'

    comma-separated-expressions ::= expression { ',' expression }

    function-params-postfix ::= '(' [ comma-separated-function-params ] ')'

    comma-separated-function-params ::= function-parameter { ',' function-parameter }

    function-parameter ::= identifier [ '=' expression ]
    | object-literal

    module-element ::= module-names
    | curly-enclosed-module-names

    paren-enclosed-string ::= '(' string-literal ')'

    curly-enclosed-module-name ::= '{' comma-separated-module-names '}'

    comma-separated-module-names ::= module-names { ',' module-names }

    module-names ::= identifier
    | identifier-as-another
    | string-literal

    identifier-as-another ::= identifier "as" identifier

    # Lexical Grammar for ECMAScript

    identifier ::= underscore-fix-ident
    | asterisk-fix-ident
    | snakecase-ident
    | camelcase-ident
    | mixedcase-ident

    asterisk-fix-ident ::= '*' [ identifier ]

    underscore-fix-ident ::= '_' [ identifier ]

    mixedcase-ident ::= letter { letter | digit | '_' }

    snakecase-ident ::= lower { lower | digit | '_' }


    camelcase-ident ::= lower { letter | digit | '_' }

    pascalcase-ident ::= upper { letter | digit | '_' }

    lexical-literal ::= bigint-literal
    | null-literal
    | boolean-literal
    | float-literal
    | integer-literal
    | string-literal
    | regex-literal

    regexp-literal ::= '/' regexp-pattern '/' { regex-flags }

    regexp-flags ::= 'g' | 'i' | 'm' | 's' | 'u' | 'y' | 'd'

    regexp-pattern ::= ? any valid js regexp pattern ?

    bigint-literal ::= { digit } 'n'

    null-literal ::= "null" | "undefined"

    boolean-literal ::= "true" | "false"

    float-literal ::= scientific-notation | rational-number

    integer-literal ::= { digit }
    | bin-prefix { bin-digit }
    | oct-prefix { oct-digit }
    | hex-prefix { hex-digit }

    string-literal ::= single-quoted-string
    | double-quoted-string

    scientific-notation ::= { digit } ( 'e' | 'E' ) [ '-' | '+' ] { digit }

    rational-number ::= [ { digit } ] '.' { digit }

    single-quoted-string ::= "'" { printable } "'"

    double-quoted-string ::= '"' { printable } '"'

    printable ::= letter | digit | punctuation | whitespace

    oct-prefix ::= '0' ( 'o' | 'O' )

    hex-preix ::= '0' ( 'x' | 'X' )

    bin-prefix ::= '0' ( 'b' | 'B' )

    hex-digit ::= digit | hex-lower | hex-upper

    hex-lower ::= 'a' | ... | 'f'

    hex-upper ::= 'A' | ... | 'F'

    oct-digit ::= '0' | ... | '7'

    bin-digit ::= '0' | '1'

    digit ::= '0' | '1' | '2' | ... | '7' | '8' | '9'

    punctuation ::= '!'| '"' | '#' | '$' | '%' | '&' | "'"
    | '(' | ')' | '*' | '+' | ',' | '-' | '.' | '/'
    | ':' | ';' | '<' | '=' | '>' | '?' | '@'
    | '[' | '\\' | ']' | '^' | '_' | '`'
    | '{' | '|' | '}' | '~'

    letter ::= uppper | lower

    upper ::= 'A' | 'B' | 'C' | ... | 'X' | 'Y' | 'Z'

    lower ::= 'a' | 'b' | 'c' | ... | 'x' | 'y' | 'z'

    escape-sequence ::= '\' char-escape
    | '\' unicode-escape
    | '\' hex-escape
    | '\' unicode-point-escape
    | '\' newline

    char-escape ::= "'" | '"' | '\' | 'n' | 'r' | 't' | 'b' | 'f' | 'v' | '0'

    unicode-escape ::= 'u' { hex-digit }

    hex-escape ::= 'x' { hex-digit }

    unicode-point-escape ::= 'u' '{' { hex-digit } '}'

    ignorable ::= whitespace
    | comment

    comment ::= single-line-comment | multi-line-comment

    multi-line-comment ::= "/*" { no-star-slash } "*/"

    no-star-slash ::= ? any character except star and forward slash ?

    single-line-comment ::= "// " { no-newline } newline

    no-newline ::= ? any chacter except newline ?

    null-join ::= ? matching nothing, a nil token, used to denote zero-with joins ?

    whitespace ::= newline
    | space
    | tabulator

    space ::= ? literal space character ?

    tabulator ::= ? literal horizontal tab character ?

    newline ::= ? lietal newline character ?
    23 changes: 23 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,23 @@
    I needed to write some sort of 'syntax transformer' for my job, long story, but genrally I needed to have a tree in the style that I wanted. A halway between an abstract syntax tree, and an 'style definition' tree. So I needed to capture the comments, the style of variables, etc. Neither Babel, nor ESPrima, and not Acorn, none of them were any help. On the other hand, I was not a master of JS, so I decided to peruse through ECMA-262 and write the EBNF grammar. I hope to feed this grammar to various AI models, and have them make tree specs and parser genrators with it.


    Meanwhile I will release this grammar under the Unlicense so people can use it for whatever they want, maybe do similar things I wish to do with it.

    Don't bother looking for an old-timy parser generator that accepts EBNF. AI models have opened a vast array of optiions on how EBNF can be utilized, that goes beyond the usage that tools like BNFC offer.

    Enjoy.


    ### How to Read EBNF Gramamrs

    It's pretty simple.

    * Ellipses mark ranges,
    * Text between two question marks mean it's a free-style 'whatever goes' type of rule,
    * Rules between parethensis mark grouping,
    * Curly braces mean sequence
    * Brackets mean optional

    Thanks bascialyl it.

    PS: In NeoVim, this was pretty formatted, every ::= was under one another. I don't know why Github editor has ruined it?