erlang / otp

Erlang/OTP
http://erlang.org
Apache License 2.0
11.41k stars 2.95k forks source link

Grammar railroad diagram #7250

Open mingodad opened 1 year ago

mingodad commented 1 year ago

After manually edit https://github.com/erlang/otp/blob/master/lib/compiler/src/core_parse.yrl to convert it to an EBNF understood by https://www.bottlecaps.de/rr/ui to generate a navigable railroad diagram (basically replace %% by //%%, -> by ::= and remove code after :).

Copy and paste the EBNF shown bellow on https://www.bottlecaps.de/rr/ui on the tab Edit Grammar the click on the tab View Diagram to see/download a navigable railroad diagram.

// From https://github.com/erlang/otp/blob/master/lib/compiler/src/core_parse.yrl
//%% -*-Erlang-*-
//%% %CopyrightBegin%
//%%
//%% Copyright Ericsson AB 1999-2021. All Rights Reserved.
//%%
//%% Licensed under the Apache License, Version 2.0 (the "License");
//%% you may not use this file except in compliance with the License.
//%% You may obtain a copy of the License at
//%%
//%%     http://www.apache.org/licenses/LICENSE-2.0
//%%
//%% Unless required by applicable law or agreed to in writing, software
//%% distributed under the License is distributed on an "AS IS" BASIS,
//%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//%% See the License for the specific language governing permissions and
//%% limitations under the License.
//%%
//%% %CopyrightEnd%
//%%
//%% Core Erlang YECC parser grammar

//%% Have explicit productions for annotated phrases named anno_XXX.
//%% This just does an XXX and adds the annotation.

//Expect 0.
//
//Nonterminals
//
//module_definition module_export module_attribute module_defs
//exported_names exported_name
//attribute_list attribute
//function_definition function_definitions
//
//constant constants atomic_constant tuple_constant cons_constant tail_constant
//other_pattern atomic_pattern tuple_pattern cons_pattern tail_pattern
//binary_pattern segment_patterns segment_pattern
//
//expression single_expression
//literal literals atomic_literal tuple_literal cons_literal tail_literal fun_literal
//nil tuple cons tail
//binary segments segment
//
//let_expr let_vars letrec_expr case_expr fun_expr
//function_name
//application_expr call_expr primop_expr arg_list
//receive_expr timeout try_expr
//sequence catch_expr
//variable clause clause_pattern
//
//map_expr anno_map_expr map_pairs anno_map_pair map_pair map_pair_assoc map_pair_exact
//map_pattern map_pair_patterns map_pair_pattern
//
//annotation anno_atom anno_fun anno_expression anno_expressions
//anno_variable anno_variables anno_pattern anno_patterns
//anno_function_name
//anno_literal
//anno_segment anno_segment_pattern
//anno_clause anno_clauses.
//
//Terminals
//
////%% Separators
//
//'(' ')' '{' '}' '[' ']' '|' ',' '->' '=' '/' '<' '>' ':' '-|' '#'
//'~' '=>' ':='
//
////%% Keywords (atoms are assumed to always be single-quoted).
//
//'module' 'attributes' 'do' 'let' 'in' 'letrec'
//'apply' 'call' 'primop'
//'case' 'of' 'end' 'when' 'fun' 'try' 'catch' 'receive' 'after'
//
////%% Literal tokens (provided by the tokeniser):
//
//char integer float atom string var.
//
////%% Literal tokens NOT provided by the tokenise:
//
//nil ::= '[' ']' : {nil,tok_line('$1')}.
//
////%% Declare the start rule for parsing
//
//Rootsymbol module_definition.

//%% Grammar

module_definition ::=
    'module' atom module_export module_attribute module_defs 'end'
module_definition ::=
    '(' 'module' atom module_export module_attribute module_defs 'end'
    '-|' annotation ')'

module_export ::= '[' ']'
module_export ::= '[' exported_names ']'

exported_names ::= exported_name ',' exported_names
exported_names ::= exported_name

exported_name ::= anno_function_name

module_attribute ::= 'attributes' '[' ']'
module_attribute ::= 'attributes' '[' attribute_list ']'

attribute_list ::= attribute ',' attribute_list
attribute_list ::= attribute

attribute ::= anno_atom '=' anno_literal

anno_atom ::= atom
anno_atom ::= '(' atom '-|' annotation ')'

anno_literal ::= literal
anno_literal ::= '(' literal '-|' annotation ')'

module_defs ::= function_definitions

annotation ::= '[' ']'
annotation ::= '[' constants ']'

function_definitions ::=
    function_definition function_definitions
function_definitions ::=
    '$empty'

function_definition ::=
    anno_function_name '=' anno_fun

anno_fun ::= '(' fun_expr '-|' annotation ')'
anno_fun ::= fun_expr

//%% Constant terms for annotations and attributes.
//%%  These are represented by straight unabstract Erlang.

constant ::= atomic_constant
constant ::= tuple_constant
constant ::= cons_constant

constants ::= constant ',' constants
constants ::= constant

atomic_constant ::= char
atomic_constant ::= integer
atomic_constant ::= float
atomic_constant ::= atom
atomic_constant ::= string
atomic_constant ::= nil

tuple_constant ::= '{' '}'
tuple_constant ::= '{' constants '}'

cons_constant ::= '[' constant tail_constant

tail_constant ::= ']'
tail_constant ::= '|' constant ']'
tail_constant ::= ',' constant tail_constant

//%% Patterns
//%%  We have to be a little sneaky here as we would like to be able to
//%%  do:
//%%  V = {a}
//%%  ( V = {a} -| <anno> )
//%%  ( V -| <anno> ) = {a}
//%%  V = ( {a} -| <anno> )
//%%  ( ( V -| <anno> ) = ( {a} -| <anno> ) -| <anno> )

anno_pattern ::= '(' other_pattern '-|' annotation ')'
anno_pattern ::= other_pattern
anno_pattern ::= anno_variable

anno_patterns ::= anno_pattern ',' anno_patterns
anno_patterns ::= anno_pattern

other_pattern ::= atomic_pattern
other_pattern ::= tuple_pattern
other_pattern ::= map_pattern
other_pattern ::= cons_pattern
other_pattern ::= binary_pattern
other_pattern ::= anno_variable '=' anno_pattern

atomic_pattern ::= atomic_literal

tuple_pattern ::= '{' '}'
tuple_pattern ::= '{' anno_patterns '}'

map_pattern ::= '~' '{' '}' '~'
map_pattern ::= '~' '{' map_pair_patterns '}' '~'
map_pattern ::= '~' '{' map_pair_patterns '|' anno_map_expr '}' '~'

map_pair_patterns ::= map_pair_pattern
map_pair_patterns ::= map_pair_pattern ',' map_pair_patterns

map_pair_pattern ::= anno_expression ':=' anno_pattern
map_pair_pattern ::= '(' anno_expression ':=' anno_pattern '-|' annotation ')'

cons_pattern ::= '[' anno_pattern tail_pattern

tail_pattern ::= ']'
tail_pattern ::= '|' anno_pattern ']'
tail_pattern ::= ',' anno_pattern tail_pattern

binary_pattern ::= '#' '{' '}' '#'
binary_pattern ::= '#' '{' segment_patterns '}' '#'

segment_patterns ::= anno_segment_pattern ',' segment_patterns
segment_patterns ::= anno_segment_pattern

anno_segment_pattern ::= segment_pattern
anno_segment_pattern ::= '(' segment_pattern '-|' annotation ')'

segment_pattern ::= '#' '<' anno_pattern '>' '(' anno_expressions ')'

variable ::= var

anno_variables ::= anno_variable ',' anno_variables
anno_variables ::= anno_variable

anno_variable ::= variable
anno_variable ::= '(' variable '-|' annotation ')'

//%% Expressions
//%%  Must split expressions into two levels as nested value expressions
//%%  are illegal.

anno_expression ::= expression
anno_expression ::= '(' expression '-|' annotation ')'

anno_expressions ::= anno_expression ',' anno_expressions
anno_expressions ::= anno_expression

expression ::= '<' '>'
expression ::= '<' anno_expressions '>'
expression ::= single_expression

single_expression ::= atomic_literal
single_expression ::= tuple
single_expression ::= cons
single_expression ::= binary
single_expression ::= variable
single_expression ::= function_name
single_expression ::= fun_literal
single_expression ::= fun_expr
single_expression ::= let_expr
single_expression ::= letrec_expr
single_expression ::= case_expr
single_expression ::= receive_expr
single_expression ::= application_expr
single_expression ::= call_expr
single_expression ::= primop_expr
single_expression ::= try_expr
single_expression ::= sequence
single_expression ::= catch_expr
single_expression ::= map_expr

literal ::= atomic_literal
literal ::= tuple_literal
literal ::= cons_literal

literals ::= literal ',' literals
literals ::= literal

atomic_literal ::= char
atomic_literal ::= integer
atomic_literal ::= float
atomic_literal ::= atom
atomic_literal ::= string
atomic_literal ::= nil

tuple_literal ::= '{' '}'
tuple_literal ::= '{' literals '}'

cons_literal ::= '[' literal tail_literal

tail_literal ::= ']'
tail_literal ::= '|' literal ']'
tail_literal ::= ',' literal tail_literal

fun_literal ::= 'fun' atom ':' atom '/' integer

tuple ::= '{' '}'
tuple ::= '{' anno_expressions '}'

map_expr ::= '~' '{' '}' '~'
map_expr ::= '~' '{' map_pairs '}' '~'
map_expr ::= '~' '{' map_pairs '|' anno_variable '}' '~'
map_expr ::= '~' '{' map_pairs '|' anno_map_expr '}' '~'

anno_map_expr ::= map_expr
anno_map_expr ::= '(' map_expr '-|' annotation ')'

map_pairs ::= anno_map_pair
map_pairs ::= anno_map_pair ',' map_pairs

anno_map_pair ::= map_pair
anno_map_pair ::= '(' map_pair '-|' annotation ')'

map_pair ::= map_pair_assoc
map_pair ::= map_pair_exact

map_pair_assoc ::= anno_expression '=>' anno_expression
map_pair_exact ::= anno_expression ':=' anno_expression

cons ::= '[' anno_expression tail

tail ::= ']'
tail ::= '|' anno_expression ']'
tail ::= ',' anno_expression tail

binary ::= '#' '{' '}' '#'
binary ::= '#' '{' segments '}' '#'

segments ::= anno_segment ',' segments
segments ::= anno_segment

anno_segment ::= segment
anno_segment ::= '(' segment '-|' annotation ')'

segment ::= '#' '<' anno_expression '>' '(' anno_expressions ')'

function_name ::= atom '/' integer

anno_function_name ::= function_name
anno_function_name ::= '(' function_name '-|' annotation ')'

let_vars ::= anno_variable
let_vars ::= '<' '>'
let_vars ::= '<' anno_variables '>'

sequence ::= 'do' anno_expression anno_expression

fun_expr ::= 'fun' '(' ')' '->' anno_expression
fun_expr ::= 'fun' '(' anno_variables ')' '->' anno_expression

let_expr ::= 'let' let_vars '=' anno_expression 'in' anno_expression

letrec_expr ::= 'letrec' function_definitions 'in' anno_expression

case_expr ::= 'case' anno_expression 'of' anno_clauses 'end'

anno_clauses ::= anno_clause anno_clauses
anno_clauses ::= anno_clause

anno_clause ::= clause
anno_clause ::= '(' clause '-|' annotation ')'

clause ::= clause_pattern 'when' anno_expression '->' anno_expression

clause_pattern ::= anno_pattern
clause_pattern ::= '<' '>'
clause_pattern ::= '<' anno_patterns '>'

application_expr ::= 'apply' anno_expression arg_list

call_expr ::=
    'call' anno_expression ':' anno_expression arg_list

primop_expr ::= 'primop' anno_expression arg_list

arg_list ::= '(' ')'
arg_list ::= '(' anno_expressions ')'

try_expr ::=
    'try' anno_expression 'of' let_vars '->' anno_expression
    'catch' let_vars '->' anno_expression

catch_expr ::= 'catch' anno_expression

receive_expr ::= 'receive' timeout
receive_expr ::= 'receive' anno_clauses timeout

timeout ::=
    'after' anno_expression '->' anno_expression

//%% ====================================================================== //%%
jhogberg commented 1 year ago

Thanks, do you want this diagram to be part of the documentation? Feel free to make a PR that adds it.

mingodad commented 1 year ago

Ideally the parser generator tool should have an option to output a naked grammar and also an EBNF grammar like the one shown above (something like I did for bison/yacc/lemon here https://github.com/mingodad/lalr-parser-test ).