Grammar overview SPIRIT
TOP PREVIOUS NEXT

The Gnuplot-style syntax is defined by statement_list in the grammar below. Omitted from this listing is the specification of expression, in order to make clear the overall structure of the language. The grammar defining expression, (to be found here) provides the rules by which expressions such as 2+3*sin(sqrt(x)/y(x)) are parsed (where x and y are a previously declared variable and function, respectively).


    statement_list      ::= (statement ('\n' | ';')+)*
    statement           ::= var_assignment
                          | function_definition
                          | "print " expression_list
    expression_list     ::= expression (',' expression)*
    var_assignment      ::= name '=' expression
    function_definition ::= name '(' name_list? ')' '=' expression
    name_list           ::= name (',' name)*
    name                ::= (alpha_p | '_') (alnum_p | '_')* 

Here, alpha_p and alnum_p are Spirit primitives meaning any alphabetic character [A-Za-z] and any character [A-Za-z0-9], respectively. Some points to note about this grammar:


    0    foo(a)=2+a
    1    foo(a,b)=a*b
    2    print foo(3)    # outputs 5
    3    print foo(3,4)  # outputs 12 

Skip grammar

The grammar definition above provides only part of the story. The rest comes from the definition of what to ignore. The Gnuplot-style syntax dictates that the skip grammar is defined as:


    skip       ::= whitespace
                 | '\\' whitespace* '\n'
                 | '#' (anychar_p - '\n')*
    whitespace ::= space_p - '\n' 

space_p and anychar_p are Spirit primitives matching any character that matches the C standard library routine isspace and any character at all, respectively.

Note that \n is used by the main grammar to signify the end of a statement. This grammar will ignore whitespace other than \n and will treat anything following a '#' as a comment.

The "\\\n" grouping is used to continue a single statement over more than one line. This continuation behaviour is less sophisticated than that found in a Unix shell but is identical to that of Gnuplot. An expression such as:


    foo(a,b) = \ # Function declaration
        a + b    # Function definition 

is illegal. This is a legal use of \:


    foo(a,b) =   # Function declaration \
        a + b    # Function definition 

but it does no more than continue the comment. (Parsing will fail thereafter because the function has no definition.)

expression

The expression grammar is essentially similar to that of C and is defined formally by expr below:


    expr          ::= logical_expr conditional_expr_helper?
    conditional_expr_helper ::= '?' expr ':' expr conditional_expr_helper?
    logical_expr  ::= bitwise_expr  (("&&" | "||") bitwise_expr)*
    bitwise_expr  ::= equality_expr (('&' | '|' | '^') equality_expr)*
    equality_expr ::= compare_expr  (("==" | "!=") compare_expr)*
    compare_expr  ::= shift_expr    (('<' | '>' | "<=" | ">=") shift_expr)*
    shift_expr    ::= add_expr      (("<<" | ">>") add_expr)*
    add_expr      ::= mult_expr     (('+' | '-') mult_expr)*
    mult_expr     ::= expr_atom     (('*' | '/' | '%') expr_atom)*
    expr_atom     ::= number
                    | function
                    | '(' expr ')'
                    | ('+' | '-' | '!') expr_atom
    number        ::= real_p | local_vars | global_vars
    function      ::= name '(' arg_list? ')'
    arg_list      ::= expr (',' expr)* 

real_p is a Spirit primitive that will match any real number. local_vars and global_vars are symbol tables of recognized local and global variables respectively. Similarly, function must be found in the symbol table of known functions for the parser to pass the input as valid.

Some points to note:

This completes the formal definition of the grammar. An implementation of this grammar using Spirit can be found here. It conforms to the definition above exactly, save for the limitation that no symbol tables are checked when parsing any variables or functions.

The code does nothing more than flag whether the input data conforms to the grammar. What it does demonstrate, however, is that writing a parser using Spirit is trivially easy once the grammar has been specified formally. The fun and games start when we try and attach semantic actions to each parsed snippet.

TOP PREVIOUS NEXT

Valid XHTML 1.1! Valid CSS!