diff options
Diffstat (limited to 'parse.cxx')
-rw-r--r-- | parse.cxx | 188 |
1 files changed, 112 insertions, 76 deletions
@@ -150,6 +150,27 @@ parser::last () } + +template <typename OPERAND> +bool eval_comparison (const OPERAND& lhs, const token* op, const OPERAND& rhs) +{ + if (op->type == tok_operator && op->content == "<=") + { return lhs <= rhs; } + else if (op->type == tok_operator && op->content == ">=") + { return lhs >= rhs; } + else if (op->type == tok_operator && op->content == "<") + { return lhs < rhs; } + else if (op->type == tok_operator && op->content == ">") + { return lhs > rhs; } + else if (op->type == tok_operator && op->content == "==") + { return lhs == rhs; } + else if (op->type == tok_operator && op->content == "!=") + { return lhs != rhs; } + else + throw parse_error ("expected comparison operator", op); +} + + // Here, we perform on-the-fly preprocessing. // The basic form is %( CONDITION %? THEN-TOKENS %: ELSE-TOKENS %) // where CONDITION is: kernel_v[r] COMPARISON-OP "version-string" @@ -159,7 +180,7 @@ parser::last () // The %: ELSE-TOKENS part is optional. // // e.g. %( kernel_v > "2.5" %? "foo" %: "baz" %) -// e.g. %( arch != "i686" %? "foo" %: "baz" %) +// e.g. %( arch != "i?86" %? "foo" %: "baz" %) // // Up to an entire %( ... %) expression is processed by a single call // to this function. Tokens included by any nested conditions are @@ -241,35 +262,19 @@ bool eval_pp_conditional (systemtap_session& s, return result; } - else if ((l->type == tok_string && r->type == tok_string) - || (l->type == tok_number && r->type == tok_number)) + else if (l->type == tok_string && r->type == tok_string) { - // collect acceptable strverscmp results. - int rvc_ok1, rvc_ok2; - if (op->type == tok_operator && op->content == "<=") - { rvc_ok1 = -1; rvc_ok2 = 0; } - else if (op->type == tok_operator && op->content == ">=") - { rvc_ok1 = 1; rvc_ok2 = 0; } - else if (op->type == tok_operator && op->content == "<") - { rvc_ok1 = -1; rvc_ok2 = -1; } - else if (op->type == tok_operator && op->content == ">") - { rvc_ok1 = 1; rvc_ok2 = 1; } - else if (op->type == tok_operator && op->content == "==") - { rvc_ok1 = 0; rvc_ok2 = 0; } - else if (op->type == tok_operator && op->content == "!=") - { rvc_ok1 = -1; rvc_ok2 = 1; } - else - throw parse_error ("expected comparison operator", op); - - int rvc_result = l->content.compare(r->content); - - // normalize rvc_result - if (rvc_result < 0) rvc_result = -1; - if (rvc_result > 0) rvc_result = 1; - + string lhs = l->content; + string rhs = r->content; + return eval_comparison (lhs, op, rhs); + // NB: no wildcarding option here + } + else if (l->type == tok_number && r->type == tok_number) + { + int64_t lhs = lex_cast<int64_t>(l->content); + int64_t rhs = lex_cast<int64_t>(r->content); + return eval_comparison (lhs, op, rhs); // NB: no wildcarding option here - - return (rvc_result == rvc_ok1 || rvc_result == rvc_ok2); } else if (l->type == tok_string && r->type == tok_number && op->type == tok_operator) @@ -277,17 +282,18 @@ bool eval_pp_conditional (systemtap_session& s, else if (l->type == tok_number && r->type == tok_string && op->type == tok_operator) throw parse_error ("expected number literal as right value", r); + // XXX: support other forms? "CONFIG_SMP" ? + else throw parse_error ("expected 'arch' or 'kernel_v' or 'kernel_vr'\n" " or comparison between strings or integers", l); } -// expand_args is used to know if we must expand $x and @x identifiers. // Only tokens corresponding to the TRUE statement must be expanded const token* -parser::scan_pp (bool wildcard, bool expand_args) +parser::scan_pp (bool wildcard) { while (true) { @@ -298,7 +304,7 @@ parser::scan_pp (bool wildcard, bool expand_args) return t; } - const token* t = input.scan (wildcard, expand_args); // NB: not recursive! + const token* t = input.scan (wildcard); // NB: not recursive! if (t == 0) // EOF return t; @@ -308,77 +314,108 @@ parser::scan_pp (bool wildcard, bool expand_args) // We have a %( - it's time to throw a preprocessing party! const token *l, *op, *r; - l = input.scan (false, expand_args); // NB: not recursive, though perhaps could be - op = input.scan (false, expand_args); - r = input.scan (false, expand_args); + l = input.scan (false); // NB: not recursive, though perhaps could be + op = input.scan (false); + r = input.scan (false); if (l == 0 || op == 0 || r == 0) throw parse_error ("incomplete condition after '%('", t); // NB: consider generalizing to consume all tokens until %?, and // passing that as a vector to an evaluator. // Do not evaluate the condition if we haven't expanded everything. - // This may occured when having several recursive conditionals. - bool result = expand_args && eval_pp_conditional (session, l, op, r); + // This may occur when having several recursive conditionals. + bool result = eval_pp_conditional (session, l, op, r); delete l; delete op; delete r; - + + /* + clog << "PP eval (" << *t << ") == " << result << endl; + */ + const token *m = input.scan (); // NB: not recursive if (! (m && m->type == tok_operator && m->content == "%?")) throw parse_error ("expected '%?' marker for conditional", t); delete m; // "%?" vector<const token*> my_enqueued_pp; - bool have_token = false; - + + int nesting = 0; while (true) // consume THEN tokens { - m = scan_pp (wildcard, result); // NB: recursive - if (m == 0) - throw parse_error (have_token ? - "incomplete conditional - missing %: or %)" : - "missing THEN tokens for conditional", - t); - - have_token = true; - if (m->type == tok_operator && (m->content == "%:" || // ELSE - m->content == "%)")) // END + try + { + m = result ? scan_pp (wildcard) : input.scan (wildcard); + } + catch (const parse_error &e) + { + if (result) throw e; // propagate errors if THEN branch taken + continue; + } + + if (m && m->type == tok_operator && m->content == "%(") // nested %( + nesting ++; + if (nesting == 0 && m && (m->type == tok_operator && (m->content == "%:" || // ELSE + m->content == "%)"))) // END break; - // enqueue token - if (result) + if (nesting && m && m->type == tok_operator && m->content == "%)") // nested %) + nesting --; + + if (!m) + throw parse_error ("incomplete conditional - missing '%:' or '%)'", t); + if (result) my_enqueued_pp.push_back (m); - else - delete m; // unused token - // continue + if (!result) + delete m; // do nothing, just dispose of unkept THEN token + + continue; } - have_token = false; if (m && m->type == tok_operator && m->content == "%:") // ELSE { delete m; // "%:" + int nesting = 0; while (true) { - m = scan_pp (wildcard, expand_args && !result); // NB: recursive - if (m == 0) - throw parse_error (have_token ? - "incomplete conditional - missing %)" : - "missing ELSE tokens for conditional", - t); - - have_token = true; - if (m->type == tok_operator && m->content == "%)") // END + try + { + m = result ? input.scan (wildcard) : scan_pp (wildcard); + } + catch (const parse_error& e) + { + if (!result) throw e; // propagate errors if ELSE branch taken + continue; + } + + if (m && m->type == tok_operator && m->content == "%(") // nested %( + nesting ++; + if (nesting == 0 && m && m->type == tok_operator && m->content == "%)") // END break; - // enqueue token - if (! result) - my_enqueued_pp.push_back (m); - else - delete m; // unused token - // continue + if (nesting && m && m->type == tok_operator && m->content == "%)") // nested %) + nesting --; + + if (!m) + throw parse_error ("incomplete conditional - missing %)", t); + if (!result) + my_enqueued_pp.push_back (m); + if (result) + delete m; // do nothing, just dispose of unkept ELSE token + + continue; } } + + /* + clog << "PP eval (" << *t << ") == " << result << " tokens: " << endl; + for (unsigned k=0; k<my_enqueued_pp.size(); k++) + clog << * my_enqueued_pp[k] << endl; + clog << endl; + */ + delete t; // "%(" delete m; // "%)" + // NB: we transcribe the retained tokens here, and not inside // the THEN/ELSE while loops. If it were done there, each loop // would become infinite (each iteration consuming an ordinary @@ -598,7 +635,7 @@ lexer::input_put (const string& chars) token* -lexer::scan (bool wildcard, bool expand_args) +lexer::scan (bool wildcard) { token* n = new token; n->location.file = input_name; @@ -633,8 +670,7 @@ lexer::scan (bool wildcard, bool expand_args) // characters; @1..@999 are quoted/escaped as strings. // $# and @# expand to the number of arguments, similarly // raw or quoted. - if (expand_args && - (c == '$' || c == '@') && + if ((c == '$' || c == '@') && (c2 == '#')) { input_get(); // swallow '#' @@ -645,8 +681,7 @@ lexer::scan (bool wildcard, bool expand_args) semiskipped_p ++; goto semiskip; } - else if (expand_args && - (c == '$' || c == '@') && + else if ((c == '$' || c == '@') && (isdigit (c2))) { unsigned idx = 0; @@ -660,7 +695,8 @@ lexer::scan (bool wildcard, bool expand_args) idx <= session.args.size()); // prevent overflow if (idx == 0 || idx-1 >= session.args.size()) - throw parse_error ("command line argument index invalid or out of range", n); + throw parse_error ("command line argument index " + lex_cast<string>(idx) + + " out of range [1-" + lex_cast<string>(session.args.size()) + "]", n); string arg = session.args[idx-1]; if (c == '$') input_put (arg); |