Skip to content

Semantics

Ronald Franco edited this page Dec 24, 2018 · 2 revisions

Attribute-Flow Grammars use flow variables to give grammar symbols a semantic meaning. As such, in this section we will discuss the use of flow variables and their manipulation through semantic actions.

Flow Variables

A flow variable is simply a declared variable. The flow variable can be of a primitive or non-primitive type. The syntax to assign flow variables to a grammar symbol is given here:

symbol(in_flow_variable)>>out_flow_variable

Grammar symbols, i.e. BaseParser objects, maintain pointers to their own flow variables. The use of pointers conserves memory and makes the implementation of flow variables for each symbol type-safe. However, when using non-primitive out-flow variables an extra step must be taken. That is, we need to overload a function in the TokenStream class.

TokenStream

The TokenStream class is used to extract the semantic value from the token lexeme and store it into the out-flow variable. More specifically, the extraction operator of the TokenStream class handles the storing of semantic information into out-flow variables. Its default definition is as follows:

template <typename OutType>
friend TokenStream<InType>& operator>>(TokenStream<InType>& in, OutType& out)
{
std::istringstream(in.text_) >> out;
return in;
}

The above extraction operator definition may raise an exception if the extraction operation between the std::istringstream object and the out-flow variable fails. In such an event, the parsing algorithm is designed to silently fail by default. This can be seen starting on line 733 of parser.h:

TokenStream<InType> tok_stream(tokens->at(pos).code, tokens->at(pos).text, in_);
try
{
tok_stream >> *out_;
} catch (parsing_error&) { return false; }

One could update the above catch block to perform some sort of recovery to continue parsing, such as fill in a default value.

Properties

  1. Instances of grammar symbols may have flow variables if its declaration has flow variables. An example of an AFG that violates this property is given below:
// formal definition of BIT has no flow variables
BIT = Token('0')
	| Token('1');

// BIT instance is defined to have an out-flow variable, illegal
IT_NUM>>z = [&]{ z = 0; } & +( BIT>>b & [&]{ z = 2 * z + b; } );
  1. Flow variables specified for the grammar symbol must be of the same types specified in the declaration of that grammar symbol. That is,
// flow variables
int a;
double b;

Parser<int,double> symbol;  // symbol must have an in-flow variable of type 
                            // int and an out-flow variable of type double

symbol(a)>>b; // legal

symbol(b)>>a; // not legal
  1. An instance of a grammar symbol may have a different set of flow variables than that of its definition, as long as the types are the same. We can modify our running AFG example to illustrate this:
// formal definition of BIT has out-flow variable c
BIT>>c = Token('0') & [&]{ c = 0; }
	| Token('1') & [&]{ c = 1; };

// BIT instance has b as its out-flow variable, legal
IT_NUM>>z = [&]{ z = 0; } & +( BIT>>b & [&]{ z = 2 * z + b; } );