1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
|
tt(%type) directives is used to associate (non-)terminals with semantic
value types. E.g., after:
verb(
%polymorphic INT: int; TEXT: std::string
%type <INT> expr
)
the tt(expr) nonterminal returns tt(int) semantic values. In a
rule like:
verb(
expr:
expr '+' expr
{
// Action block: C++ statements here.
}
)
Inside action blocks $-notations can be used to retrieve and assign values
from/to the elements of production rules. Type directives are used to
associates $-notations with polymorphic semantic types. In the example $$, $1
and $3 are automatically associated with tt(int) values. $$ is associated with
the rule (here:, `tt(expr:)'; it is the semantic value that becomes available
once the production rule has been recognized), while $1 and $3 are tt(int)
values which are associated with, resp., the first and third element (the
tt(expr) nonterminals) in the production rule tt(expr '+' expr).
Negative dollar indices can also be used (like $-1), but then pre-defined
associations between nonterminals and semantic types are not available, and
must explicitly be specified to access their polymorphic value types. Blanks
cannot be used for type-specifications (so $<INT>-1 is OK, $< INT >-1 is not).
Various $-constructions are available to indicate values of rule-elements. $$
constructions refer to values of nonterminals, $1 constructions refer to
values of numbered elements of production rules (the examples use $1, but in
general the positive number must be less than the element number of the action
block in which it is used. Likewise, $-1 constructions refer to values of
elements of production rules that are specified before the current rule's
nonterminal, and in theory any negative number could be used.
The term `value of' indicates the generic tt(STYPE_) value of an
element. When a rule element is associated with a polymorphic type, then the
term `semantic value of' is used.
Here is an overview of the various possibilities:
itemization(
itt($$ = )
A semantic value is assigned to the rule. The right-hand side (rhs) of
the assignment expression must be an expression of the type that is associated
with $$. This assignment operation assumes that the type of the rhs-expression
equals $$'s semantic value type. If the types don't match the compiler issues
a compilation error when compiling tt(parse.cc). Casting the rhs to the
correct value type is possible, but in that case the function call operator is
preferred, which does not require casting. If no semantic value type was
associated with $$ then the assignment tt($$ = STYPE_{}) can be used.
itt($$(expr))
A semantic value is assigned to the rule. tt(Expr) must be of a type that
can statically be cast to $$'s semantic value type. The required
tt(static_cast) is generated by bic() and doesn't have to be specified for
tt(expr).
itt(_$$)
This refers to the rule's return value (i.e., tt(d_val_)). No allowance
is made for associated types.
itt($$)
If no semantic value was associated with $$ then this is shorthand for a
reference to the rule's value. If a semantic value type was associated with $$
then a reference to the rule's semantic value is returned.
itt($$.)
If no semantic value was associated with $$ then this is shorthand for the
member selector operator, applied to a reference to the rule's return
value. If a semantic value type was associated with $$ then the member
selector operator is applied to the rule's semantic value.
itt($$->)
If no semantic value was associated with $$ then this is shorthand for the
pointer to member operator, applied to the rule's return value. If a semantic
value type was associated with $$ then the pointer to member operator is
applied to the rule's semantic value.
itt(_$1)
This refers to the variable containing the semantic value of the current
production rule's first element. No allowance is made for associated types.
itt($1)
This construction refers to the semantic value of the production rule's
first element. If no semantic value was associated with $1 then this is
shorthand for the tt(STYPE_) value that's available for the production rule's
element 1. If a semantic value was associated with $1 then $1 refers to the
semantic value associated with the production rule's element 1.
itt($1.)
If no semantic value was associated with $1 then this is shorthand for the
member selector operator, applied to the semantic value of the production
rule's element 1. If a semantic value was associated with $1 then the member
selector operator is applied to the semantic value associated with the
production rule's element 1.
itt($1->)
If no semantic value was associated with $1 then this is shorthand for the
pointer to member operator, applied to the semantic value of the production
rule's element 1. If a semantic value was associated with $1 then the pointer
to member operator is applied to the semantic value associated with the
production rule's element 1.
itt(_$-1)
This refers to the value of some production rule element, 1 element before
the current rule's nonterminal. No allowance is made for associated semantic
types.
itt($-1)
Same: this refers to the value of some production rule element, 1 element
before the current rule's nonterminal. No allowance is made for associated
semantic types.
itt($-1.)
This is shorthand for the member selector operator applied to to the value
of some production rule element, 1 element before the current rule's
nonterminal. No allowance is made for associated semantic types.
itt($1->)
This is shorthand for the pointer to member operator applied to to the
value of some production rule element, 1 element before the current rule's
nonterminal. No allowance is made for associated semantic types.
itt($<tag>-1)
This refers to the semantic value of a polymorphic type (associated with
tt(tag)) of some production rule element, 1 element before the current rule's
nonterminal.
If the semantic value type of that element doesn't match the type that is
associated with tt(tag) then a run-time fatal error results. If that happens,
and the tt(debug) option/directive was specified when bic() wrote the
program's tt(parse) member, then the program can be rerun after specifying
tt(parser.setDebug(Parser::ACTIONCASES)) to locate the tt(parse) function's
action block where the fatal error was encountered.
itt($<tag>-1.)
This refers to the member selector operator applied to the semantic value
of a polymorphic type (associated with tt(tag)) of some production rule
element, 1 element before the current rule's nonterminal.
If the semantic value type of that element doesn't match the type that is
associated with tt(tag) then a run-time fatal error results. The procedure
suggested at the previous tt($<tag>-1) item for solving such errors can be
applied here as well.
itt($<tag>-1->)
This refers to the pointer to member operator applied to the semantic
value of a polymorphic type (associated with tt(tag)) of some production rule
element, 1 element before the current rule's nonterminal.
If the semantic value type of that element doesn't match the type that is
associated with tt(tag) then a run-time fatal error results. The procedure
suggested at the previous tt($<tag>-1) item for solving such errors can be
applied here as well.
)
COMMENT(
whenhtml(
center(
table(1)(l)(
rowline()
row(cell(center(includefile(polytable))))
))
includefile(polytablenotes)
rowline()
)
whenman(
bf(%type<TAG> and $$ or $1 specifications:)
includefile(polytable)
includefile(polytablenotes)
)
bf(Direct semantic value assignments) (`$$(args)'):
Production rules associated with polymorphic tags and production rules
associated with tt(%type <STYPE_>) should assign values of matching types to
the rules' semantic values. If a matching value type is available from a
production rule's component it can directly be used to assign the semantic
value of the rule's left-hand side nonterminal. E.g., $$ = $1 returns the
semantic value of the production rule's first component.
However, often production rules receiving tokens from the lexical scanner
cannot refer to semantic values of components of their production rules, as
the scanner merely returns the token and matching text, as in the following
production rule:
verb(
number: // assume %type<NUMBER> number
NUMBER
{
// convert text to a numeric value
}
;
)
In such cases the appropriate semantic value must be initialized.
Bic() translates phrases like tt($$ = xyz) into tt(d_val_.get<tag>() =
xyz) (where tt(tag) is the production rule's left-hand side nonterminal's
polymorphic tag, and tt(d_val_) (see below) is the parser's data member
containing the action block's semantic value). The function tt(get) returns a
reference to a value of the type matching tt(tag), but that value isn't
available yet: in this case it is the action block's duty to initialize it.
Unless there's comment at either side of the assignment operator, bic()
recognizes such situations, and issues a warning, and such warnings should be
prevented lest the generated tt(parse) function produce a segmentation
fault.
Fortunately, prevention is easy. To initialize a rule's semantic value a
tt($$(args)) expression should be used (no blanks are allowed between
tt($$) and the open-parenthesis). tt(Args) are at least one, comma-separated
arguments. For tagged nonterminals bic() translates a tt($$(args))
expression into
verb(
d_val_.assign<tag>(args).
)
where `tt(tag)' is the tag associated with the production rule's left-hand
side nonterminal. For nonterminals that are associated with a
tt(%type<STYPE_>) specification only one argument can be passed to
tt($$(arg)), which translates to
verb(
d_val_ = (arg);
)
The tt($$(args)) syntax cannot be used for untagged production rules.
If a nonterminal is associated with tt(%type<STYPE_>) and its production
rule calls a function returning a value of a known tagged type, then an
explicit tt(assign) call can be used. E.g.,
verb(
Symbol useSymbol(); // available function
%polymorphic SYMBOL: Symbol; // directives
%type<STYPE_> rule
rule: // grammar rule
IDENTIFIER
{
$$.assign<Tag_::SYMBOL>(useSymbol());
}
;
bf(Member calls) (`$$.', `$1.', `($$)', `($1)', etc.):)
When `$$.' or `$1.' is used default tags are ignored. A warning is issued
that the default tag is ignored. This syntax allows members of the semantic
value type (tt(STYPE_)) to be called explicitly. The default tag is only
ignored if there are no additional characters (e.g., blanks, closing
parentheses) between the dollar-expressions and the member selector operator
(e.g., no tags are used with $1.member(), but tags are used with
tt(($1).member())). In fact, notations like tt(($$), ($1)), etc. are synonym
to using tt($$.get<Tag_::TYPE>(), $1.get<Tag_::TYPE>())
The opposite, overriding default tag associations, is
accomplished using constructions like $<STYPE_>$ and $<STYPE_>1.
When negative dollar indices are used, the appropriate tag must explicitly be
specified. The next example shows how this is realized in the grammar
specification file itself:
verb(
%polymorphic INT: int
%type <INT> ident
%%
type:
ident arg
;
arg:
{
call($-1.get<Tag_::INT>());
}
;
)
In this example tt(call) may define an tt(int) or tt(int &) parameter.
It is also possible to delegate specification of the semantic value to the
function tt(call) itself, as shown next:
verb(
%polymorphic INT: int
%type <INT> ident
%%
type:
ident arg
;
arg:
{
call($-1);
}
;
)
Here, the function tt(call) could be implemented like this:
verb(
void call(STYPE_ &st)
{
st.get<Tag_::INT>() = 5;
}
)
Semantic values may also directly be associated with terminal tokens. In that
case it is the lexical scanner's responsibility to assign a properly typed
value to the parser's tt(STYPE_ d_val_) data member. When the lexical
scanner receives a pointer to the parser's tt(d_val_) data member (using,
e.g., a member tt(setSval(STYPE_ *dval)))
IFDEF(manual)((cf. section ref(PRIVDATA)))(),
then the lecical scanner must use em(tagged assignment) as shown in the above
example to reach the different polymorphic types. The lexical scanner, having
defined a tt(Parser::STYPE_ *d_val) data member could then use statements
like
verb(
d_val.assign<Tag_::INT>(stoi(matched()));
)
to assign an tt(int) value to the parser's semantic value, which is then
available to the parser when the lexical scanner's tt(lex) function returns as
the semantic value (e.g., $1) of the production rule's element specifying the
terminal token. Note, however that this requires the scanner to harbor some
intelligence about the meaning of a tt(Parser::INT) token. It can be argued
that this intelligence should only be available to the parser, and that the
scanner should merely recognize regular expressions and return tokens and
their corresponding matched text.
END)
|