@@grammar::BEL
@@parseinfo :: True

##############
#  METADATA  #
##############

# BEL VERSION: {{ bel_version }}
# EBNF CREATION TIMESTAMP: {{ created_time }}

#########################
#  GRAMMAR DEFINITIONS  #
#########################

start = bel_statement $ ;
bel_statement = subject:function ~ [relation:relation ~ object:obj] ;

obj = (function | enclosed_statement) ;
enclosed_statement = function_open bel_statement:bel_statement function_close ;

##### FUNCTIONS #####

function = function:funcs function_open function_args:f_args function_close ;
funcs =
    {% for function in functions %}'{{ function }}'{{ " | " if not loop.last }}{% endfor %} ;

f_args = ','.{(function | modifier_function | namespace_arg | string_arg)}* ;

##### MODIFIER FUNCTIONS #####

modifier_function = modifier:m_funcs function_open modifier_args:m_args function_close ;
m_funcs =
    {% for modifier in m_functions %}'{{ modifier }}'{{ " | " if not loop.last }}{% endfor %} ;

m_args = ','.{(function | namespace_arg | string_arg)}* ;

##### RELATIONSHIPS #####

relation = relations ;
relations =
    {% for relation in relations %}'{{ relation }}'{{ " | " if not loop.last }}{% endfor %} ;

##### MISCELLANEOUS #####

namespace_arg = ns_arg:full_nsv ;
full_nsv = ns:ns_string ':' ns_value:(quoted_string | string) ;

string_arg = str_arg:full_string ;
full_string = (quoted_string | string) ;



# quoted_string: Matches like any string but requires it to be in quotation marks.
quoted_string = /\"(?:[^"\\]|\\.)*\"/ ;

# string: Matches any char other than space, comma or ')'
string = /[^\s\),]+/ ;

# ns_string: Matches any capital letter or digit.
ns_string = /[A-Z0-9]+/ ;

function_open = '(' ;
function_close = ')' ;
