parse.cxx - systemtap
Data types defined
Functions defined
Source code
#include "config.h"
#include "staptree.h"
#include "parse.h"
#include "session.h"
#include "util.h"
#include <iostream>
#include <fstream>
#include <cctype>
#include <cstdlib>
#include <cassert>
#include <cerrno>
#include <climits>
#include <sstream>
#include <cstring>
#include <cctype>
#include <iterator>
extern "C" {
#include <fnmatch.h>
}
using namespace std;
class lexer
{
public:
bool ate_comment; bool ate_whitespace; bool saw_tokens; bool check_compatible;
token* scan ();
lexer (istream&, const string&, systemtap_session&);
void set_current_file (stapfile* f);
void set_current_token_chain (const token* tok);
inline bool has_version (const char* v) const;
static set<string> keywords;
static set<string> atwords;
private:
inline int input_get ();
inline int input_peek (unsigned n=0);
void input_put (const string&, const token*);
string input_name;
string input_contents;
const char *input_pointer; const char *input_end;
unsigned cursor_suspend_count;
unsigned cursor_suspend_line;
unsigned cursor_suspend_column;
unsigned cursor_line;
unsigned cursor_column;
systemtap_session& session;
stapfile* current_file;
const token* current_token_chain;
};
class parser
{
public:
parser (systemtap_session& s, const string& n, istream& i, unsigned flags=0);
~parser ();
stapfile* parse ();
probe* parse_synthetic_probe (const token* chain);
stapfile* parse_library_macros ();
private:
typedef enum {
PP_NONE,
PP_KEEP_THEN,
PP_SKIP_THEN,
PP_KEEP_ELSE,
PP_SKIP_ELSE,
} pp_state_t;
struct pp1_activation;
struct pp_macrodecl : public macrodecl {
pp1_activation* parent_act; virtual bool is_closure() { return parent_act != 0; }
pp_macrodecl () : macrodecl(), parent_act(0) { }
};
systemtap_session& session;
string input_name;
lexer input;
bool errs_as_warnings;
bool privileged;
parse_context context;
struct pp1_activation {
const token* tok;
unsigned cursor; map<string, pp_macrodecl*> params;
macrodecl* curr_macro;
pp1_activation (const token* tok, macrodecl* curr_macro)
: tok(tok), cursor(0), curr_macro(curr_macro) { }
~pp1_activation ();
};
map<string, macrodecl*> pp1_namespace;
vector<pp1_activation*> pp1_state;
const token* next_pp1 ();
const token* scan_pp1 ();
const token* slurp_pp1_param (vector<const token*>& param);
const token* slurp_pp1_body (vector<const token*>& body);
vector<pair<const token*, pp_state_t> > pp_state;
const token* scan_pp ();
const token* skip_pp ();
const token* next ();
const token* peek ();
void swallow ();
const token* systemtap_v_seen;
const token* last_t; const token* next_t;
void expect_known (token_type tt, string const & expected);
void expect_unknown (token_type tt, string & target);
void expect_unknown2 (token_type tt1, token_type tt2, string & target);
void expect_op (string const & expected);
void expect_kw (string const & expected);
void expect_number (int64_t & expected);
void expect_ident_or_keyword (string & target);
bool peek_op (string const & op);
bool peek_kw (string const & kw);
const token* expect_kw_token (string const & expected);
const token* expect_ident_or_atword (string & target);
void print_error (const parse_error& pe, bool errs_as_warnings = false);
unsigned num_errors;
private: void parse_probe (vector<probe*>&, vector<probe_alias*>&);
void parse_global (vector<vardecl*>&, vector<probe*>&);
void parse_functiondecl (vector<functiondecl*>&);
embeddedcode* parse_embeddedcode ();
probe_point* parse_probe_point ();
literal_string* consume_string_literals (const token*);
literal_string* parse_literal_string ();
literal* parse_literal ();
block* parse_stmt_block ();
try_block* parse_try_block ();
statement* parse_statement ();
if_statement* parse_if_statement ();
for_loop* parse_for_loop ();
for_loop* parse_while_loop ();
foreach_loop* parse_foreach_loop ();
expr_statement* parse_expr_statement ();
return_statement* parse_return_statement ();
delete_statement* parse_delete_statement ();
next_statement* parse_next_statement ();
break_statement* parse_break_statement ();
continue_statement* parse_continue_statement ();
indexable* parse_indexable ();
const token *parse_hist_op_or_bare_name (hist_op *&hop, string &name);
target_symbol *parse_target_symbol ();
cast_op *parse_cast_op ();
atvar_op *parse_atvar_op ();
expression* parse_entry_op (const token* t);
expression* parse_defined_op (const token* t);
expression* parse_perf_op (const token* t);
expression* parse_expression ();
expression* parse_assignment ();
expression* parse_ternary ();
expression* parse_logical_or ();
expression* parse_logical_and ();
expression* parse_boolean_or ();
expression* parse_boolean_xor ();
expression* parse_boolean_and ();
expression* parse_array_in ();
expression* parse_comparison_or_regex_query ();
expression* parse_shift ();
expression* parse_concatenation ();
expression* parse_additive ();
expression* parse_multiplicative ();
expression* parse_unary ();
expression* parse_crement ();
expression* parse_dwarf_value ();
expression* parse_value ();
expression* parse_symbol ();
bool peek_target_symbol_components ();
void parse_target_symbol_components (target_symbol* e);
};
stapfile*
parse (systemtap_session& s, const string& n, istream& i, unsigned flags)
{
parser p (s, n, i, flags);
return p.parse ();
}
stapfile*
parse (systemtap_session& s, const string& name, unsigned flags)
{
ifstream i(name.c_str(), ios::in);
if (i.fail())
{
cerr << (file_exists(name)
? _F("Input file '%s' can't be opened for reading.", name.c_str())
: _F("Input file '%s' is missing.", name.c_str()))
<< endl;
return 0;
}
parser p (s, name, i, flags);
return p.parse ();
}
stapfile*
parse_library_macros (systemtap_session& s, const string& name)
{
ifstream i(name.c_str(), ios::in);
if (i.fail())
{
cerr << (file_exists(name)
? _F("Input file '%s' can't be opened for reading.", name.c_str())
: _F("Input file '%s' is missing.", name.c_str()))
<< endl;
return 0;
}
parser p (s, name, i);
return p.parse_library_macros ();
}
probe*
parse_synthetic_probe (systemtap_session &s, std::istream& i, const token* tok)
{
parser p (s, "<synthetic>", i);
return p.parse_synthetic_probe (tok);
}
parser::parser (systemtap_session& s, const string &n, istream& i, unsigned flags):
session (s), input_name (n), input (i, input_name, s),
errs_as_warnings(flags & pf_squash_errors), privileged (flags & pf_guru),
context(con_unknown), systemtap_v_seen(0), last_t (0), next_t (0), num_errors (0)
{
input.check_compatible = !(flags & pf_no_compatible);
}
parser::~parser()
{
}
static string
tt2str(token_type tt)
{
switch (tt)
{
case tok_junk: return "junk";
case tok_identifier: return "identifier";
case tok_operator: return "operator";
case tok_string: return "string";
case tok_number: return "number";
case tok_embedded: return "embedded-code";
case tok_keyword: return "keyword";
}
return "unknown token";
}
ostream&
operator << (ostream& o, const source_loc& loc)
{
o << loc.file->name << ":"
<< loc.line << ":"
<< loc.column;
return o;
}
ostream&
operator << (ostream& o, const token& t)
{
o << tt2str(t.type);
if (t.type != tok_embedded && t.type != tok_keyword) XXX {
o << " '";
for (unsigned i=0; i<t.content.length(); i++)
{
char c = t.content[i];
o << (isprint (c) ? c : '?');
}
o << "'";
}
o << " at "
<< t.location;
return o;
}
void
parser::print_error (const parse_error &pe, bool errs_as_warnings)
{
const token *tok = pe.tok ? pe.tok : last_t;
session.print_error(pe, tok, input_name, errs_as_warnings);
num_errors ++;
}
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);
}
macrodecl::~macrodecl ()
{
delete tok;
for (vector<const token*>::iterator it = body.begin();
it != body.end(); it++)
delete *it;
}
parser::pp1_activation::~pp1_activation ()
{
delete tok;
if (curr_macro->is_closure()) return; for (map<string, pp_macrodecl*>::iterator it = params.begin();
it != params.end(); it++)
delete it->second;
}
const token*
parser::next_pp1 ()
{
if (pp1_state.empty())
return input.scan ();
pp1_activation* act = pp1_state.back();
unsigned& cursor = act->cursor;
if (cursor < act->curr_macro->body.size())
{
token* t = new token(*act->curr_macro->body[cursor]);
t->chain = new token(*act->tok); cursor++;
return t;
}
else
return 0; }
const token*
parser::scan_pp1 ()
{
while (true)
{
const token* t = next_pp1 ();
if (t == 0) {
if (pp1_state.empty()) return 0;
pp1_activation* act = pp1_state.back();
pp1_state.pop_back(); delete act;
continue;
}
if (t->type == tok_operator && t->content == "@define")
{
if (!pp1_state.empty())
throw PARSE_ERROR (_("'@define' forbidden inside macro body"), t);
delete t;
t = input.scan();
if (! (t && t->type == tok_identifier))
throw PARSE_ERROR (_("expected identifier"), t);
string name = t->content;
if (pp1_namespace.find(name) != pp1_namespace.end())
{
parse_error er (ERR_SRC, _F("attempt to redefine macro '@%s' in the same file", name.c_str ()), t);
er.chain = new PARSE_ERROR (_F("macro '@%s' first defined here",
name.c_str()), pp1_namespace[name]->tok);
throw er;
}
XXX
XXX if (name == "define")
throw PARSE_ERROR (_("attempt to redefine '@define'"), t);
if (input.atwords.count("@" + name))
session.print_warning (_F("macro redefines built-in operator '@%s'", name.c_str()), t);
macrodecl* decl = (pp1_namespace[name] = new macrodecl);
decl->tok = t;
bool saw_params = false;
t = input.scan();
if (t && t->type == tok_operator && t->content == "(")
{
saw_params = true;
do
{
delete t;
t = input.scan ();
if (! (t && t->type == tok_identifier))
throw PARSE_ERROR(_("expected identifier"), t);
decl->formal_args.push_back(t->content);
delete t;
t = input.scan ();
if (t && t->type == tok_operator && t->content == ",")
{
continue;
}
else if (t && t->type == tok_operator && t->content == ")")
{
delete t;
t = input.scan();
break;
}
else
{
throw PARSE_ERROR (_("expected ',' or ')'"), t);
}
}
while (true);
}
if (! (t && t->type == tok_operator && t->content == "%("))
{
if (saw_params)
throw PARSE_ERROR (_("expected '%('"), t);
else
throw PARSE_ERROR (_("expected '%(' or '('"), t);
}
delete t;
t = slurp_pp1_body (decl->body);
if (!t)
throw PARSE_ERROR (_("incomplete macro definition - missing '%)'"), decl->tok);
delete t;
continue;
}
if (t->type == tok_operator && t->content[0] == '@')
{
string name = t->content.substr(1);
macrodecl* decl;
pp1_activation* act = pp1_state.empty() ? 0 : pp1_state.back();
if (act && act->params.find(name) != act->params.end())
decl = act->params[name];
else if (!(act && act->curr_macro->context == ctx_library)
&& pp1_namespace.find(name) != pp1_namespace.end())
decl = pp1_namespace[name];
else if (session.library_macros.find(name)
!= session.library_macros.end())
decl = session.library_macros[name];
else return t;
pp1_activation *new_act = new pp1_activation(t, decl);
unsigned num_params = decl->formal_args.size();
if (num_params == 0 && decl->is_closure())
{
new_act->params = ((pp_macrodecl*)decl)->parent_act->params;
goto expand;
}
if (num_params == 0)
goto expand;
t = next_pp1 ();
if (! (t && t->type == tok_operator && t->content == "("))
{
delete new_act;
throw PARSE_ERROR (_NF
("expected '(' in invocation of macro '@%s'"
" taking %d parameter",
"expected '(' in invocation of macro '@%s'"
" taking %d parameters",
num_params, name.c_str(), num_params), t);
}
XXX for (unsigned i = 0; i < num_params; i++)
{
delete t;
string param_name = decl->formal_args[i];
pp_macrodecl* p = (new_act->params[param_name]
= new pp_macrodecl);
p->tok = new token(*new_act->tok);
p->parent_act = act;
t = slurp_pp1_param (p->body);
if (t == 0) {
XXX const token* orig_t = new token(*new_act->tok);
delete new_act;
throw PARSE_ERROR (_("could not find end of macro invocation"), orig_t);
}
if (t->type == tok_operator && t->content == ",")
{
if (i + 1 == num_params)
{
delete new_act;
throw PARSE_ERROR (_F("too many parameters for macro '@%s' (expected %d)", name.c_str(), num_params), t);
}
}
else if (t->type == tok_operator && t->content == ")")
{
if (i + 1 != num_params)
{
delete new_act;
throw PARSE_ERROR (_F("too few parameters for macro '@%s' (expected %d)", name.c_str(), num_params), t);
}
}
else
{
XXX delete new_act;
throw PARSE_ERROR(_("expected ',' or ')' after macro parameter"), t);
}
}
delete t;
expand:
pp1_state.push_back (new_act);
continue;
}
return t;
}
}
const token*
parser::slurp_pp1_param (vector<const token*>& param)
{
const token* t = 0;
unsigned nesting = 0;
do
{
t = next_pp1 ();
if (!t)
break;
if (t->type == tok_operator && t->content == "(")
++nesting;
else if (nesting && t->type == tok_operator && t->content == ")")
--nesting;
else if (!nesting && t->type == tok_operator
&& (t->content == ")" || t->content == ","))
break;
param.push_back(t);
}
while (true);
return t; }
const token*
parser::slurp_pp1_body (vector<const token*>& body)
{
const token* t = 0;
unsigned nesting = 0;
do
{
t = next_pp1 ();
if (!t)
break;
if (t->type == tok_operator && t->content == "%(")
++nesting;
else if (nesting && t->type == tok_operator && t->content == "%)")
--nesting;
else if (!nesting && t->type == tok_operator && t->content == "%)")
break;
body.push_back(t);
}
while (true);
return t; }
stapfile*
parser::parse_library_macros ()
{
stapfile* f = new stapfile;
input.set_current_file (f);
try
{
const token* t = scan_pp1 ();
XXX
if (t != 0)
throw PARSE_ERROR (_F("library macro file '%s' contains non-@define construct", input_name.c_str()), t);
for (map<string, macrodecl*>::iterator it = pp1_namespace.begin();
it != pp1_namespace.end(); it++)
{
string name = it->first;
if (session.library_macros.find(name) != session.library_macros.end())
{
parse_error er(ERR_SRC, _F("duplicate definition of library macro '@%s'", name.c_str()), it->second->tok);
er.chain = new PARSE_ERROR (_F("macro '@%s' first defined here", name.c_str()), session.library_macros[name]->tok);
print_error (er);
delete er.chain;
delete f;
return 0;
}
}
}
catch (const parse_error& pe)
{
print_error (pe, errs_as_warnings);
delete f;
return 0;
}
for (map<string, macrodecl*>::iterator it = pp1_namespace.begin();
it != pp1_namespace.end(); it++)
{
string name = it->first;
session.library_macros[name] = it->second;
session.library_macros[name]->context = ctx_library;
}
return f;
}
bool eval_pp_conditional (systemtap_session& s,
const token* l, const token* op, const token* r)
{
if (l->type == tok_identifier && (l->content == "kernel_v" ||
l->content == "kernel_vr" ||
l->content == "systemtap_v"))
{
if (! (r->type == tok_string))
throw PARSE_ERROR (_("expected string literal"), r);
string target_kernel_vr = s.kernel_release;
string target_kernel_v = s.kernel_base_release;
string target;
if (l->content == "kernel_v") target = target_kernel_v;
else if (l->content == "kernel_vr") target = target_kernel_vr;
else if (l->content == "systemtap_v") target = s.compatible;
else assert (0);
string query = r->content;
bool rhs_wildcard = (strpbrk (query.c_str(), "*?[") != 0);
int rvc_ok1, rvc_ok2;
bool wc_ok = false;
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; wc_ok = true; }
else if (op->type == tok_operator && op->content == "!=")
{ rvc_ok1 = -1; rvc_ok2 = 1; wc_ok = true; }
else
throw PARSE_ERROR (_("expected comparison operator"), op);
if ((!wc_ok) && rhs_wildcard)
throw PARSE_ERROR (_("wildcard not allowed with order comparison operators"), op);
if (rhs_wildcard)
{
int rvc_result = fnmatch (query.c_str(), target.c_str(),
FNM_NOESCAPE); bool badness = (rvc_result == 0) ^ (op->content == "==");
return !badness;
}
else
{
int rvc_result = strverscmp (target.c_str(), query.c_str());
if (rvc_result < 0) rvc_result = -1;
if (rvc_result > 0) rvc_result = 1;
return (rvc_result == rvc_ok1 || rvc_result == rvc_ok2);
}
}
else if (l->type == tok_identifier && l->content == "systemtap_privilege")
{
string target_privilege =
pr_contains(s.privilege, pr_stapdev) ? "stapdev"
: pr_contains(s.privilege, pr_stapsys) ? "stapsys"
: pr_contains(s.privilege, pr_stapusr) ? "stapusr"
: "none"; assert(target_privilege != "none");
if (! (r->type == tok_string))
throw PARSE_ERROR (_("expected string literal"), r);
string query_privilege = r->content;
bool nomatch = (target_privilege != query_privilege);
bool result;
if (op->type == tok_operator && op->content == "==")
result = !nomatch;
else if (op->type == tok_operator && op->content == "!=")
result = nomatch;
else
throw PARSE_ERROR (_("expected '==' or '!='"), op);
XXX
return result;
}
else if (l->type == tok_identifier && l->content == "guru_mode")
{
if (! (r->type == tok_number))
throw PARSE_ERROR (_("expected number"), r);
int64_t lhs = (int64_t) s.guru_mode;
int64_t rhs = lex_cast<int64_t>(r->content);
if (!((rhs == 0)||(rhs == 1)))
throw PARSE_ERROR (_("expected 0 or 1"), op);
if (!((op->type == tok_operator && op->content == "==") ||
(op->type == tok_operator && op->content == "!=")))
throw PARSE_ERROR (_("expected '==' or '!='"), op);
return eval_comparison (lhs, op, rhs);
}
else if (l->type == tok_identifier && l->content == "arch")
{
string target_architecture = s.architecture;
if (! (r->type == tok_string))
throw PARSE_ERROR (_("expected string literal"), r);
string query_architecture = r->content;
int nomatch = fnmatch (query_architecture.c_str(),
target_architecture.c_str(),
FNM_NOESCAPE);
bool result;
if (op->type == tok_operator && op->content == "==")
result = !nomatch;
else if (op->type == tok_operator && op->content == "!=")
result = nomatch;
else
throw PARSE_ERROR (_("expected '==' or '!='"), op);
return result;
}
else if (l->type == tok_identifier && l->content == "runtime")
{
if (! (r->type == tok_string))
throw PARSE_ERROR (_("expected string literal"), r);
string query_runtime = r->content;
string target_runtime;
target_runtime = (s.runtime_mode == systemtap_session::dyninst_runtime
? "dyninst" : "kernel");
int nomatch = fnmatch (query_runtime.c_str(),
target_runtime.c_str(),
FNM_NOESCAPE);
bool result;
if (op->type == tok_operator && op->content == "==")
result = !nomatch;
else if (op->type == tok_operator && op->content == "!=")
result = nomatch;
else
throw PARSE_ERROR (_("expected '==' or '!='"), op);
return result;
}
else if (l->type == tok_identifier && startswith(l->content, "CONFIG_"))
{
if (r->type == tok_string)
{
string lhs = s.kernel_config[l->content]; string rhs = r->content;
int nomatch = fnmatch (rhs.c_str(), lhs.c_str(), FNM_NOESCAPE);
bool result;
if (op->type == tok_operator && op->content == "==")
result = !nomatch;
else if (op->type == tok_operator && op->content == "!=")
result = nomatch;
else
throw PARSE_ERROR (_("expected '==' or '!='"), op);
return result;
}
else if (r->type == tok_number)
{
const char* startp = s.kernel_config[l->content].c_str ();
char* endp = (char*) startp;
errno = 0;
int64_t lhs = (int64_t) strtoll (startp, & endp, 0);
if (errno == ERANGE || errno == EINVAL || *endp != '\0')
throw PARSE_ERROR ("Config option value not a number", l);
int64_t rhs = lex_cast<int64_t>(r->content);
return eval_comparison (lhs, op, rhs);
}
else if (r->type == tok_identifier
&& startswith(r->content, "CONFIG_"))
{
const char* startp = s.kernel_config[l->content].c_str ();
char* endp = (char*) startp;
errno = 0;
int64_t val = (int64_t) strtoll (startp, & endp, 0);
if (errno != ERANGE && errno != EINVAL && *endp == '\0')
{
int64_t lhs = val;
startp = s.kernel_config[r->content].c_str ();
endp = (char*) startp;
errno = 0;
int64_t rhs = (int64_t) strtoll (startp, & endp, 0);
if (errno != ERANGE && errno != EINVAL && *endp == '\0')
return eval_comparison (lhs, op, rhs);
}
string lhs = s.kernel_config[l->content];
string rhs = s.kernel_config[r->content];
return eval_comparison (lhs, op, rhs);
}
else
throw PARSE_ERROR (_("expected string, number literal or other CONFIG_... as right side operand"), r);
}
else if (l->type == tok_string && r->type == tok_string)
{
string lhs = l->content;
string rhs = r->content;
return eval_comparison (lhs, op, rhs);
}
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);
}
else if (l->type == tok_string && r->type == tok_number
&& op->type == tok_operator)
throw PARSE_ERROR (_("expected string literal as right value"), r);
else if (l->type == tok_number && r->type == tok_string
&& op->type == tok_operator)
throw PARSE_ERROR (_("expected number literal as right value"), r);
else
throw PARSE_ERROR (_("expected 'arch', 'kernel_v', 'kernel_vr', 'systemtap_v',\n"
" 'runtime', 'systemtap_privilege', 'CONFIG_...', or\n"
" comparison between strings or integers"), l);
}
const token*
parser::scan_pp ()
{
while (true)
{
pp_state_t pp = PP_NONE;
if (!pp_state.empty())
pp = pp_state.back().second;
const token* t = 0;
if (pp == PP_SKIP_THEN || pp == PP_SKIP_ELSE)
t = skip_pp ();
else
t = scan_pp1 ();
if (t == 0) {
if (pp != PP_NONE)
{
t = pp_state.back().first;
pp_state.pop_back(); throw PARSE_ERROR (_("incomplete conditional at end of file"), t);
}
return t;
}
if (t->type == tok_operator && t->content == "%?")
throw PARSE_ERROR (_("incomplete conditional - missing '%('"), t);
if (t->type == tok_operator && t->content == "%:")
{
if (pp == PP_NONE)
throw PARSE_ERROR (_("incomplete conditional - missing '%('"), t);
if (pp == PP_KEEP_ELSE || pp == PP_SKIP_ELSE)
throw PARSE_ERROR (_("invalid conditional - duplicate '%:'"), t);
XXX
pp_state.back().second = (pp == PP_KEEP_THEN) ?
PP_SKIP_ELSE : PP_KEEP_ELSE;
delete t;
continue;
}
if (t->type == tok_operator && t->content == "%)")
{
if (pp == PP_NONE)
throw PARSE_ERROR (_("incomplete conditional - missing '%('"), t);
delete pp_state.back().first;
delete t; pp_state.pop_back();
continue;
}
if (! (t->type == tok_operator && t->content == "%(")) return t;
bool result = false;
bool and_result = true;
const token *n = NULL;
do {
const token *l, *op, *r;
l = scan_pp1 ();
op = scan_pp1 ();
r = scan_pp1 ();
if (l == 0 || op == 0 || r == 0)
throw PARSE_ERROR (_("incomplete condition after '%('"), t);
and_result &= eval_pp_conditional (session, l, op, r);
if(l->content=="systemtap_v")
systemtap_v_seen=r;
else
delete r;
delete l;
delete op;
delete n;
n = scan_pp1 ();
if (n && n->type == tok_operator && n->content == "&&")
continue;
result |= and_result;
and_result = true;
if (! (n && n->type == tok_operator && n->content == "||"))
break;
} while (true);
const token *m = n;
if (! (m && m->type == tok_operator && m->content == "%?"))
throw PARSE_ERROR (_("expected '%?' marker for conditional"), t);
delete m;
pp = result ? PP_KEEP_THEN : PP_SKIP_THEN;
pp_state.push_back (make_pair (t, pp));
}
}
const token*
parser::skip_pp ()
{
const token* t = 0;
unsigned nesting = 0;
do
{
try
{
t = scan_pp1 ();
}
catch (const parse_error &e)
{
continue;
}
if (!t)
break;
if (t->type == tok_operator && t->content == "%(")
++nesting;
else if (nesting && t->type == tok_operator && t->content == "%)")
--nesting;
else if (!nesting && t->type == tok_operator &&
(t->content == "%:" || t->content == "%?" || t->content == "%)"))
break;
delete t;
}
while (true);
return t;
}
const token*
parser::next ()
{
if (! next_t)
next_t = scan_pp ();
if (! next_t)
throw PARSE_ERROR (_("unexpected end-of-file"));
last_t = next_t;
next_t = 0;
return last_t;
}
const token*
parser::peek ()
{
if (! next_t)
next_t = scan_pp ();
last_t = next_t;
return next_t;
}
void
parser::swallow ()
{
assert (last_t != 0);
delete last_t;
last_t = next_t = 0;
}
static inline bool
tok_is(token const * t, token_type tt, string const & expected)
{
return t && t->type == tt && t->content == expected;
}
void
parser::expect_known (token_type tt, string const & expected)
{
const token *t = next();
if (! (t && t->type == tt && t->content == expected))
throw PARSE_ERROR (_F("expected '%s'", expected.c_str()));
swallow (); }
void
parser::expect_unknown (token_type tt, string & target)
{
const token *t = next();
if (!(t && t->type == tt))
throw PARSE_ERROR (_("expected ") + tt2str(tt));
target = t->content;
swallow (); }
void
parser::expect_unknown2 (token_type tt1, token_type tt2, string & target)
{
const token *t = next();
if (!(t && (t->type == tt1 || t->type == tt2)))
throw PARSE_ERROR (_F("expected %s or %s", tt2str(tt1).c_str(), tt2str(tt2).c_str()));
target = t->content;
swallow (); }
void
parser::expect_op (std::string const & expected)
{
expect_known (tok_operator, expected);
}
void
parser::expect_kw (std::string const & expected)
{
expect_known (tok_keyword, expected);
}
const token*
parser::expect_kw_token (std::string const & expected)
{
const token *t = next();
if (! (t && t->type == tok_keyword && t->content == expected))
throw PARSE_ERROR (_F("expected '%s'", expected.c_str()));
return t;
}
void
parser::expect_number (int64_t & value)
{
bool neg = false;
const token *t = next();
if (t->type == tok_operator && t->content == "-")
{
neg = true;
swallow ();
t = next ();
}
if (!(t && t->type == tok_number))
throw PARSE_ERROR (_("expected number"));
const char* startp = t->content.c_str ();
char* endp = (char*) startp;
errno = 0;
value = (int64_t) strtoull (startp, & endp, 0);
if (errno == ERANGE || errno == EINVAL || *endp != '\0'
|| (neg && (unsigned long long) value > 9223372036854775808ULL)
|| (unsigned long long) value > 18446744073709551615ULL
|| value < -9223372036854775807LL-1)
throw PARSE_ERROR (_("number invalid or out of range"));
if (neg)
value = -value;
swallow (); }
const token*
parser::expect_ident_or_atword (std::string & target)
{
const token *t = next();
if (!t || (t->type != tok_identifier
&& (t->type != tok_operator || t->content[0] != '@')))
XXX throw PARSE_ERROR (_F("expected %s or statistical operation", tt2str(tok_identifier).c_str()));
target = t->content;
return t;
}
void
parser::expect_ident_or_keyword (std::string & target)
{
expect_unknown2 (tok_identifier, tok_keyword, target);
}
bool
parser::peek_op (std::string const & op)
{
return tok_is (peek(), tok_operator, op);
}
bool
parser::peek_kw (std::string const & kw)
{
return tok_is (peek(), tok_identifier, kw);
}
lexer::lexer (istream& input, const string& in, systemtap_session& s):
ate_comment(false), ate_whitespace(false), saw_tokens(false), check_compatible(true),
input_name (in), input_pointer (0), input_end (0), cursor_suspend_count(0),
cursor_suspend_line (1), cursor_suspend_column (1), cursor_line (1),
cursor_column (1), session(s), current_file (0), current_token_chain (0)
{
getline(input, input_contents, '\0');
input_pointer = input_contents.data();
input_end = input_contents.data() + input_contents.size();
if (keywords.empty())
{
keywords.insert("probe");
keywords.insert("global");
keywords.insert("function");
keywords.insert("if");
keywords.insert("else");
keywords.insert("for");
keywords.insert("foreach");
keywords.insert("in");
keywords.insert("limit");
keywords.insert("return");
keywords.insert("delete");
keywords.insert("while");
keywords.insert("break");
keywords.insert("continue");
keywords.insert("next");
keywords.insert("string");
keywords.insert("long");
keywords.insert("try");
keywords.insert("catch");
}
if (atwords.empty())
{
atwords.insert("@cast");
atwords.insert("@defined");
atwords.insert("@entry");
atwords.insert("@perf");
atwords.insert("@var");
atwords.insert("@avg");
atwords.insert("@count");
atwords.insert("@sum");
atwords.insert("@min");
atwords.insert("@max");
atwords.insert("@hist_linear");
atwords.insert("@hist_log");
}
}
set<string> lexer::keywords;
set<string> lexer::atwords;
void
lexer::set_current_file (stapfile* f)
{
current_file = f;
if (f)
{
f->file_contents = input_contents;
f->name = input_name;
}
}
void
lexer::set_current_token_chain (const token* tok)
{
current_token_chain = tok;
}
int
lexer::input_peek (unsigned n)
{
if (input_pointer + n >= input_end)
return -1; return (unsigned char)*(input_pointer + n);
}
bool
lexer::has_version (const char* v) const
{
return check_compatible
? strverscmp(session.compatible.c_str(), v) >= 0
: true;
}
int
lexer::input_get ()
{
int c = input_peek();
if (c < 0) return c;
++input_pointer;
if (cursor_suspend_count)
{
if (--cursor_suspend_count == 0)
{
cursor_line = cursor_suspend_line;
cursor_column = cursor_suspend_column;
}
}
else
{
if (c == '\n')
{
cursor_line ++;
cursor_column = 1;
}
else
cursor_column ++;
}
return c;
}
void
lexer::input_put (const string& chars, const token* t)
{
size_t pos = input_pointer - input_contents.data();
input_contents.insert (pos, chars);
cursor_suspend_count += chars.size();
cursor_suspend_line = cursor_line;
cursor_suspend_column = cursor_column;
cursor_line = t->location.line;
cursor_column = t->location.column;
input_pointer = input_contents.data() + pos;
input_end = input_contents.data() + input_contents.size();
}
token*
lexer::scan ()
{
ate_comment = false; ate_whitespace = false;
XXX bool old_saw_tokens = saw_tokens;
saw_tokens = true;
token* n = new token;
n->location.file = current_file;
n->chain = current_token_chain;
skip:
bool suspended = (cursor_suspend_count > 0);
n->location.line = cursor_line;
n->location.column = cursor_column;
int c = input_get();
if (c < 0)
{
delete n;
saw_tokens = old_saw_tokens;
return 0;
}
if (isspace (c))
{
ate_whitespace = true;
goto skip;
}
int c2 = input_peek ();
if ((c == '$' || c == '@') && (c2 == '#'))
{
n->content.push_back (c);
n->content.push_back (c2);
input_get(); if (suspended)
{
n->make_junk(_("invalid nested substitution of command line arguments"));
return n;
}
size_t num_args = session.args.size ();
input_put ((c == '$') ? lex_cast (num_args) : lex_cast_qstring (num_args), n);
n->content.clear();
goto skip;
}
else if ((c == '$' || c == '@') && (isdigit (c2)))
{
n->content.push_back (c);
unsigned idx = 0;
do
{
input_get ();
idx = (idx * 10) + (c2 - '0');
n->content.push_back (c2);
c2 = input_peek ();
} while (c2 > 0 &&
isdigit (c2) &&
idx <= session.args.size()); if (suspended)
{
n->make_junk(_("invalid nested substitution of command line arguments"));
return n;
}
if (idx == 0 ||
idx-1 >= session.args.size())
{
n->make_junk(_F("command line argument index %lu out of range [1-%lu]",
(unsigned long) idx, (unsigned long) session.args.size()));
return n;
}
const string& arg = session.args[idx-1];
input_put ((c == '$') ? arg : lex_cast_qstring (arg), n);
n->content.clear();
goto skip;
}
else if (isalpha (c) || c == '$' || c == '@' || c == '_')
{
n->type = tok_identifier;
n->content = (char) c;
while (isalnum (c2) || c2 == '_' || c2 == '$')
{
input_get ();
n->content.push_back (c2);
c2 = input_peek ();
}
if (keywords.count(n->content))
n->type = tok_keyword;
else if (n->content[0] == '@')
n->type = tok_operator;
return n;
}
else if (isdigit (c)) {
n->type = tok_number;
n->content = (char) c;
while (isalnum (c2))
{
input_get ();
n->content.push_back (c2);
c2 = input_peek ();
}
return n;
}
else if (c == '\"')
{
n->type = tok_string;
while (1)
{
c = input_get ();
if (c < 0 || c == '\n')
{
n->make_junk(_("Could not find matching closing quote"));
return n;
}
if (c == '\"') break;
else if (c == '\\') {
c = input_get ();
switch (c)
{
case 'x':
if (!has_version("2.3"))
goto the_default;
case 'a':
case 'b':
case 't':
case 'n':
case 'v':
case 'f':
case 'r':
case '0' ... '7': case '\\':
XXX n->content.push_back('\\');
default: the_default:
n->content.push_back(c);
break;
}
}
else
n->content.push_back(c);
}
return n;
}
else if (ispunct (c))
{
int c3 = input_peek (1);
if (c == '#') {
unsigned this_line = cursor_line;
do { c = input_get (); }
while (c >= 0 && cursor_line == this_line);
ate_comment = true;
ate_whitespace = true;
goto skip;
}
else if ((c == '/' && c2 == '/')) {
unsigned this_line = cursor_line;
do { c = input_get (); }
while (c >= 0 && cursor_line == this_line);
ate_comment = true;
ate_whitespace = true;
goto skip;
}
else if (c == '/' && c2 == '*') {
(void) input_get (); c = input_get ();
c2 = input_get ();
while (c2 >= 0)
{
if (c == '*' && c2 == '/')
break;
c = c2;
c2 = input_get ();
}
ate_comment = true;
ate_whitespace = true;
goto skip;
}
else if (c == '%' && c2 == '{') {
n->type = tok_embedded;
(void) input_get (); c = input_get ();
c2 = input_get ();
while (c2 >= 0)
{
if (c == '%' && c2 == '}')
return n;
if (c == '}' && c2 == '%') session.print_warning (_("possible erroneous closing '}%', use '%}'?"), n);
n->content += c;
c = c2;
c2 = input_get ();
}
n->make_junk(_("Could not find matching '%}' to close embedded function block"));
return n;
}
n->type = tok_operator;
n->content = c;
if ((c == '<' && c2 == '<' && c3 == '<') ||
(c == '<' && c2 == '<' && c3 == '=') ||
(c == '>' && c2 == '>' && c3 == '='))
{
n->content += c2;
n->content += c3;
input_get (); input_get (); }
else if ((c == '=' && c2 == '=') ||
(c == '!' && c2 == '=') ||
(c == '<' && c2 == '=') ||
(c == '>' && c2 == '=') ||
(c == '=' && c2 == '~') ||
(c == '!' && c2 == '~') ||
(c == '+' && c2 == '=') ||
(c == '-' && c2 == '=') ||
(c == '*' && c2 == '=') ||
(c == '/' && c2 == '=') ||
(c == '%' && c2 == '=') ||
(c == '&' && c2 == '=') ||
(c == '^' && c2 == '=') ||
(c == '|' && c2 == '=') ||
(c == '.' && c2 == '=') ||
(c == '&' && c2 == '&') ||
(c == '|' && c2 == '|') ||
(c == '+' && c2 == '+') ||
(c == '-' && c2 == '-') ||
(c == '-' && c2 == '>') ||
(c == '<' && c2 == '<') ||
(c == '>' && c2 == '>') ||
(c == '%' && c2 == '(') ||
(c == '%' && c2 == '?') ||
(c == '%' && c2 == ':') ||
(c == '%' && c2 == ')'))
{
n->content += c2;
input_get (); }
return n;
}
else
{
n->type = tok_junk;
ostringstream s;
s << "\\x" << hex << setw(2) << setfill('0') << c;
n->content = s.str();
n->msg = ""; return n;
}
}
void
token::make_junk (const string new_msg)
{
type = tok_junk;
msg = new_msg;
}
stapfile*
parser::parse ()
{
stapfile* f = new stapfile;
input.set_current_file (f);
bool empty = true;
while (1)
{
try
{
systemtap_v_seen = 0;
const token* t = peek ();
if (! t) break;
empty = false;
if (t->type == tok_keyword && t->content == "probe")
{
context = con_probe;
parse_probe (f->probes, f->aliases);
}
else if (t->type == tok_keyword && t->content == "global")
{
context = con_global;
parse_global (f->globals, f->probes);
}
else if (t->type == tok_keyword && t->content == "function")
{
context = con_function;
parse_functiondecl (f->functions);
}
else if (t->type == tok_embedded)
{
context = con_embedded;
f->embeds.push_back (parse_embeddedcode ());
}
else
{
context = con_unknown;
throw PARSE_ERROR (_("expected 'probe', 'global', 'function', or '%{'"));
}
}
catch (parse_error& pe)
{
print_error (pe, errs_as_warnings);
XXX if (pe.skip_some) while (1)
try
{
{
const token* t = peek ();
if (! t)
break;
if (t->type == tok_keyword && t->content == "probe") break;
else if (t->type == tok_keyword && t->content == "global") break;
else if (t->type == tok_keyword && t->content == "function") break;
else if (t->type == tok_embedded) break;
swallow (); }
}
catch (parse_error& pe2)
{
print_error (pe2);
}
}
}
if (empty)
{
cerr << (input.saw_tokens
? _F("Input file '%s' is empty after preprocessing.", input_name.c_str())
: _F("Input file '%s' is empty.", input_name.c_str()))
<< endl;
delete f;
f = 0;
}
else if (num_errors > 0)
{
cerr << _NF("%d parse error.", "%d parse errors.", num_errors, num_errors) << endl;
delete f;
f = 0;
}
input.set_current_file(0);
return f;
}
probe*
parser::parse_synthetic_probe (const token* chain)
{
probe* p = NULL;
stapfile* f = new stapfile;
f->synthetic = true;
input.set_current_file (f);
input.set_current_token_chain (chain);
try
{
context = con_probe;
parse_probe (f->probes, f->aliases);
if (f->probes.size() != 1 || !f->aliases.empty())
throw PARSE_ERROR (_("expected a single synthetic probe"));
p = f->probes[0];
}
catch (parse_error& pe)
{
print_error (pe, errs_as_warnings);
}
TODO
input.set_current_file(0);
input.set_current_token_chain(0);
return p;
}
void
parser::parse_probe (std::vector<probe *> & probe_ret,
std::vector<probe_alias *> & alias_ret)
{
const token* t0 = next ();
if (! (t0->type == tok_keyword && t0->content == "probe"))
throw PARSE_ERROR (_("expected 'probe'"));
vector<probe_point *> aliases;
vector<probe_point *> locations;
bool equals_ok = true;
int epilogue_alias = 0;
while (1)
{
probe_point * pp = parse_probe_point ();
const token* t = peek ();
if (equals_ok && t
&& t->type == tok_operator && t->content == "=")
{
if (pp->optional || pp->sufficient)
throw PARSE_ERROR (_("probe point alias name cannot be optional nor sufficient"), pp->components.front()->tok);
aliases.push_back(pp);
swallow ();
continue;
}
else if (equals_ok && t
&& t->type == tok_operator && t->content == "+=")
{
if (pp->optional || pp->sufficient)
throw PARSE_ERROR (_("probe point alias name cannot be optional nor sufficient"), pp->components.front()->tok);
aliases.push_back(pp);
epilogue_alias = 1;
swallow ();
continue;
}
else if (t && t->type == tok_operator && t->content == ",")
{
locations.push_back(pp);
equals_ok = false;
swallow ();
continue;
}
else if (t && t->type == tok_operator && t->content == "{")
{
locations.push_back(pp);
break;
}
else
throw PARSE_ERROR (_("expected probe point specifier"));
}
if (aliases.empty())
{
probe* p = new probe;
p->tok = t0;
p->locations = locations;
p->body = parse_stmt_block ();
p->privileged = privileged;
p->systemtap_v_conditional = systemtap_v_seen;
probe_ret.push_back (p);
}
else
{
probe_alias* p = new probe_alias (aliases);
if(epilogue_alias)
p->epilogue_style = true;
else
p->epilogue_style = false;
p->tok = t0;
p->locations = locations;
p->body = parse_stmt_block ();
p->privileged = privileged;
p->systemtap_v_conditional = systemtap_v_seen;
alias_ret.push_back (p);
}
}
embeddedcode*
parser::parse_embeddedcode ()
{
embeddedcode* e = new embeddedcode;
const token* t = next ();
if (t->type != tok_embedded)
throw PARSE_ERROR (_("expected '%{'"));
if (! privileged)
throw PARSE_ERROR (_("embedded code in unprivileged script; need stap -g"),
false );
e->tok = t;
e->code = t->content;
return e;
}
block*
parser::parse_stmt_block ()
{
block* pb = new block;
const token* t = next ();
if (! (t->type == tok_operator && t->content == "{"))
throw PARSE_ERROR (_("expected '{'"));
pb->tok = t;
while (1)
{
t = peek ();
if (t && t->type == tok_operator && t->content == "}")
{
swallow ();
break;
}
pb->statements.push_back (parse_statement ());
}
return pb;
}
try_block*
parser::parse_try_block ()
{
try_block* pb = new try_block;
pb->tok = expect_kw_token ("try");
pb->try_block = parse_stmt_block();
expect_kw ("catch");
const token* t = peek ();
if (t != NULL && t->type == tok_operator && t->content == "(")
{
swallow ();
t = next();
if (! (t->type == tok_identifier))
throw PARSE_ERROR (_("expected identifier"));
symbol* sym = new symbol;
sym->tok = t;
sym->name = t->content;
pb->catch_error_var = sym;
expect_op (")");
}
else
pb->catch_error_var = 0;
pb->catch_block = parse_stmt_block();
return pb;
}
statement*
parser::parse_statement ()
{
statement *ret;
const token* t = peek ();
if (t && t->type == tok_operator && t->content == ";")
return new null_statement (next ());
else if (t && t->type == tok_operator && t->content == "{")
return parse_stmt_block (); else if (t && t->type == tok_keyword && t->content == "try")
return parse_try_block (); else if (t && t->type == tok_keyword && t->content == "if")
return parse_if_statement (); else if (t && t->type == tok_keyword && t->content == "for")
return parse_for_loop (); else if (t && t->type == tok_keyword && t->content == "foreach")
return parse_foreach_loop (); else if (t && t->type == tok_keyword && t->content == "while")
return parse_while_loop (); else if (t && t->type == tok_keyword && t->content == "return")
ret = parse_return_statement ();
else if (t && t->type == tok_keyword && t->content == "delete")
ret = parse_delete_statement ();
else if (t && t->type == tok_keyword && t->content == "break")
ret = parse_break_statement ();
else if (t && t->type == tok_keyword && t->content == "continue")
ret = parse_continue_statement ();
else if (t && t->type == tok_keyword && t->content == "next")
ret = parse_next_statement ();
else if (t && (t->type == tok_operator || t->type == tok_identifier ||
t->type == tok_number ||
t->type == tok_string ||
t->type == tok_embedded ))
ret = parse_expr_statement ();
XXX else
throw PARSE_ERROR (_("expected statement"));
t = peek ();
if (t && t->type == tok_operator && t->content == ";")
{
swallow (); }
return ret;
}
void
parser::parse_global (vector <vardecl*>& globals, vector<probe*>&)
{
const token* t0 = next ();
if (! (t0->type == tok_keyword && t0->content == "global"))
throw PARSE_ERROR (_("expected 'global'"));
swallow ();
while (1)
{
const token* t = next ();
if (! (t->type == tok_identifier))
throw PARSE_ERROR (_("expected identifier"));
for (unsigned i=0; i<globals.size(); i++)
if (globals[i]->name == t->content)
throw PARSE_ERROR (_("duplicate global name"));
vardecl* d = new vardecl;
d->name = t->content;
d->tok = t;
d->systemtap_v_conditional = systemtap_v_seen;
globals.push_back (d);
t = peek ();
if(t && t->type == tok_operator && t->content == "%") {
d->wrap = true;
swallow ();
t = peek();
}
if (t && t->type == tok_operator && t->content == "[") {
int64_t size;
swallow ();
expect_number(size);
if (size <= 0 || size > 1000000) throw PARSE_ERROR(_("array size out of range"));
d->maxsize = (int)size;
expect_known(tok_operator, "]");
t = peek ();
}
if (t && t->type == tok_operator && t->content == "=") {
if (!d->compatible_arity(0))
throw PARSE_ERROR(_("only scalar globals can be initialized"));
d->set_arity(0, t);
next (); d->init = parse_literal ();
d->type = d->init->type;
t = peek ();
}
if (t && t->type == tok_operator && t->content == ";") {
swallow ();
break;
}
if (t && t->type == tok_operator && t->content == ",") {
swallow ();
continue;
}
else
break;
}
}
void
parser::parse_functiondecl (std::vector<functiondecl*>& functions)
{
const token* t = next ();
if (! (t->type == tok_keyword && t->content == "function"))
throw PARSE_ERROR (_("expected 'function'"));
swallow ();
t = next ();
if (! (t->type == tok_identifier)
&& ! (t->type == tok_keyword
&& (t->content == "string" || t->content == "long")))
throw PARSE_ERROR (_("expected identifier"));
for (unsigned i=0; i<functions.size(); i++)
if (functions[i]->name == t->content)
throw PARSE_ERROR (_("duplicate function name"));
functiondecl *fd = new functiondecl ();
fd->name = t->content;
fd->tok = t;
t = next ();
if (t->type == tok_operator && t->content == ":")
{
swallow ();
t = next ();
if (t->type == tok_keyword && t->content == "string")
fd->type = pe_string;
else if (t->type == tok_keyword && t->content == "long")
fd->type = pe_long;
else throw PARSE_ERROR (_("expected 'string' or 'long'"));
swallow ();
t = next ();
}
if (! (t->type == tok_operator && t->content == "("))
throw PARSE_ERROR (_("expected '('"));
swallow ();
while (1)
{
t = next ();
if (t->type == tok_operator && t->content == ")")
{
swallow ();
break;
}
else if (! (t->type == tok_identifier))
throw PARSE_ERROR (_("expected identifier"));
vardecl* vd = new vardecl;
vd->name = t->content;
vd->tok = t;
fd->formal_args.push_back (vd);
fd->systemtap_v_conditional = systemtap_v_seen;
t = next ();
if (t->type == tok_operator && t->content == ":")
{
swallow ();
t = next ();
if (t->type == tok_keyword && t->content == "string")
vd->type = pe_string;
else if (t->type == tok_keyword && t->content == "long")
vd->type = pe_long;
else throw PARSE_ERROR (_("expected 'string' or 'long'"));
swallow ();
t = next ();
}
if (t->type == tok_operator && t->content == ")")
{
swallow ();
break;
}
if (t->type == tok_operator && t->content == ",")
{
swallow ();
continue;
}
else
throw PARSE_ERROR (_("expected ',' or ')'"));
}
t = peek ();
if (t && t->type == tok_embedded)
fd->body = parse_embeddedcode ();
else
fd->body = parse_stmt_block ();
functions.push_back (fd);
}
probe_point*
parser::parse_probe_point ()
{
probe_point* pl = new probe_point;
while (1)
{
const token* t = next ();
if (! (t->type == tok_identifier
|| t->type == tok_keyword
|| (t->type == tok_operator && t->content == "*")))
throw PARSE_ERROR (_("expected identifier or '*'"));
string content = t->content;
while (1)
{
const token* u = peek();
if (u == NULL)
break;
if (input.ate_whitespace)
break;
if (! (u->type == tok_identifier
|| u->type == tok_keyword
|| (u->type == tok_operator && u->content == "*")))
break;
content = content + u->content;
swallow ();
}
token* new_t = new token(*t);
new_t->content = content;
delete t; t = new_t;
probe_point::component* c = new probe_point::component;
c->functor = t->content;
c->tok = t;
pl->components.push_back (c);
t = peek ();
if (t && t->type == tok_operator && t->content == "(")
{
swallow (); c->arg = parse_literal ();
t = next ();
if (! (t->type == tok_operator && t->content == ")"))
throw PARSE_ERROR (_("expected ')'"));
swallow ();
t = peek ();
}
if (t && t->type == tok_operator && t->content == ".")
{
swallow ();
continue;
}
if (t && t->type == tok_operator &&
(t->content == "?" || t->content == "!"))
{
pl->optional = true;
if (t->content == "!") pl->sufficient = true;
swallow ();
t = peek ();
}
if (t && t->type == tok_keyword && t->content == "if")
{
swallow ();
t = peek ();
if (!(t && t->type == tok_operator && t->content == "("))
throw PARSE_ERROR (_("expected '('"));
swallow ();
pl->condition = parse_expression ();
t = peek ();
if (!(t && t->type == tok_operator && t->content == ")"))
throw PARSE_ERROR (_("expected ')'"));
swallow ();
t = peek ();
}
if (t && t->type == tok_operator
&& (t->content == "{" || t->content == "," ||
t->content == "=" || t->content == "+=" ))
break;
throw PARSE_ERROR (_("expected one of '. , ( ? ! { = +='"));
}
return pl;
}
literal_string*
parser::consume_string_literals(const token *t)
{
literal_string *ls = new literal_string (t->content);
const token *n = peek();
while (n != NULL && n->type == tok_string
&& ! (!input.has_version("2.0") && input.ate_comment))
{
ls->value.append(next()->content); n = peek();
}
return ls;
}
literal_string*
parser::parse_literal_string ()
{
const token* t = next ();
literal_string* l;
if (t->type == tok_string)
l = consume_string_literals (t);
else
throw PARSE_ERROR (_("expected literal string"));
l->tok = t;
return l;
}
literal*
parser::parse_literal ()
{
const token* t = next ();
literal* l;
if (t->type == tok_string)
{
l = consume_string_literals (t);
}
else
{
bool neg = false;
if (t->type == tok_operator && t->content == "-")
{
neg = true;
swallow ();
t = next ();
}
if (t->type == tok_number)
{
const char* startp = t->content.c_str ();
char* endp = (char*) startp;
errno = 0;
long long value = (long long) strtoull (startp, & endp, 0);
if (errno == ERANGE || errno == EINVAL || *endp != '\0'
|| (neg && (unsigned long long) value > 9223372036854775808ULL)
|| (unsigned long long) value > 18446744073709551615ULL
|| value < -9223372036854775807LL-1)
throw PARSE_ERROR (_("number invalid or out of range"));
if (neg)
value = -value;
l = new literal_number (value);
}
else
throw PARSE_ERROR (_("expected literal string or number"));
}
l->tok = t;
return l;
}
if_statement*
parser::parse_if_statement ()
{
const token* t = next ();
if (! (t->type == tok_keyword && t->content == "if"))
throw PARSE_ERROR (_("expected 'if'"));
if_statement* s = new if_statement;
s->tok = t;
t = next ();
if (! (t->type == tok_operator && t->content == "("))
throw PARSE_ERROR (_("expected '('"));
swallow ();
s->condition = parse_expression ();
t = next ();
if (! (t->type == tok_operator && t->content == ")"))
throw PARSE_ERROR (_("expected ')'"));
swallow ();
s->thenblock = parse_statement ();
t = peek ();
if (t && t->type == tok_keyword && t->content == "else")
{
swallow ();
s->elseblock = parse_statement ();
}
else
s->elseblock = 0;
return s;
}
expr_statement*
parser::parse_expr_statement ()
{
expr_statement *es = new expr_statement;
const token* t = peek ();
if (t == NULL)
throw PARSE_ERROR (_("expression statement expected"));
es->tok = new token (*t);
es->value = parse_expression ();
return es;
}
return_statement*
parser::parse_return_statement ()
{
const token* t = next ();
if (! (t->type == tok_keyword && t->content == "return"))
throw PARSE_ERROR (_("expected 'return'"));
if (context != con_function)
throw PARSE_ERROR (_("found 'return' not in function context"));
return_statement* s = new return_statement;
s->tok = t;
s->value = parse_expression ();
return s;
}
delete_statement*
parser::parse_delete_statement ()
{
const token* t = next ();
if (! (t->type == tok_keyword && t->content == "delete"))
throw PARSE_ERROR (_("expected 'delete'"));
delete_statement* s = new delete_statement;
s->tok = t;
s->value = parse_expression ();
return s;
}
next_statement*
parser::parse_next_statement ()
{
const token* t = next ();
if (! (t->type == tok_keyword && t->content == "next"))
throw PARSE_ERROR (_("expected 'next'"));
if (context != con_probe)
throw PARSE_ERROR (_("found 'next' not in probe context"));
next_statement* s = new next_statement;
s->tok = t;
return s;
}
break_statement*
parser::parse_break_statement ()
{
const token* t = next ();
if (! (t->type == tok_keyword && t->content == "break"))
throw PARSE_ERROR (_("expected 'break'"));
break_statement* s = new break_statement;
s->tok = t;
return s;
}
continue_statement*
parser::parse_continue_statement ()
{
const token* t = next ();
if (! (t->type == tok_keyword && t->content == "continue"))
throw PARSE_ERROR (_("expected 'continue'"));
continue_statement* s = new continue_statement;
s->tok = t;
return s;
}
for_loop*
parser::parse_for_loop ()
{
const token* t = next ();
if (! (t->type == tok_keyword && t->content == "for"))
throw PARSE_ERROR (_("expected 'for'"));
for_loop* s = new for_loop;
s->tok = t;
t = next ();
if (! (t->type == tok_operator && t->content == "("))
throw PARSE_ERROR (_("expected '('"));
swallow ();
t = peek ();
if (t && t->type == tok_operator && t->content == ";")
{
s->init = 0;
swallow ();
}
else
{
s->init = parse_expr_statement ();
t = next ();
if (! (t->type == tok_operator && t->content == ";"))
throw PARSE_ERROR (_("expected ';'"));
swallow ();
}
t = peek ();
if (t && t->type == tok_operator && t->content == ";")
{
literal_number* l = new literal_number(1);
s->cond = l;
s->cond->tok = next ();
}
else
{
s->cond = parse_expression ();
t = next ();
if (! (t->type == tok_operator && t->content == ";"))
throw PARSE_ERROR (_("expected ';'"));
swallow ();
}
t = peek ();
if (t && t->type == tok_operator && t->content == ")")
{
s->incr = 0;
swallow ();
}
else
{
s->incr = parse_expr_statement ();
t = next ();
if (! (t->type == tok_operator && t->content == ")"))
throw PARSE_ERROR (_("expected ')'"));
swallow ();
}
s->block = parse_statement ();
return s;
}
for_loop*
parser::parse_while_loop ()
{
const token* t = next ();
if (! (t->type == tok_keyword && t->content == "while"))
throw PARSE_ERROR (_("expected 'while'"));
for_loop* s = new for_loop;
s->tok = t;
t = next ();
if (! (t->type == tok_operator && t->content == "("))
throw PARSE_ERROR (_("expected '('"));
swallow ();
s->init = 0;
s->incr = 0;
s->cond = parse_expression ();
t = next ();
if (! (t->type == tok_operator && t->content == ")"))
throw PARSE_ERROR (_("expected ')'"));
swallow ();
s->block = parse_statement ();
return s;
}
foreach_loop*
parser::parse_foreach_loop ()
{
const token* t = next ();
if (! (t->type == tok_keyword && t->content == "foreach"))
throw PARSE_ERROR (_("expected 'foreach'"));
foreach_loop* s = new foreach_loop;
s->tok = t;
s->sort_direction = 0;
s->sort_aggr = sc_none;
s->value = NULL;
s->limit = NULL;
t = next ();
if (! (t->type == tok_operator && t->content == "("))
throw PARSE_ERROR (_("expected '('"));
swallow ();
symbol* lookahead_sym = NULL;
int lookahead_sort = 0;
t = peek ();
if (t && t->type == tok_identifier)
{
next ();
lookahead_sym = new symbol;
lookahead_sym->tok = t;
lookahead_sym->name = t->content;
t = peek ();
if (t && t->type == tok_operator &&
(t->content == "+" || t->content == "-"))
{
lookahead_sort = (t->content == "+") ? 1 : -1;
swallow ();
}
t = peek ();
if (t && t->type == tok_operator && t->content == "=")
{
swallow ();
s->value = lookahead_sym;
if (lookahead_sort)
{
s->sort_direction = lookahead_sort;
s->sort_column = 0;
}
lookahead_sym = NULL;
}
}
bool parenthesized = false;
t = peek ();
if (!lookahead_sym && t && t->type == tok_operator && t->content == "[")
{
swallow ();
parenthesized = true;
}
if (lookahead_sym)
{
s->indexes.push_back (lookahead_sym);
if (lookahead_sort)
{
s->sort_direction = lookahead_sort;
s->sort_column = 1;
}
lookahead_sym = NULL;
}
else while (1)
{
t = next ();
if (! (t->type == tok_identifier))
throw PARSE_ERROR (_("expected identifier"));
symbol* sym = new symbol;
sym->tok = t;
sym->name = t->content;
s->indexes.push_back (sym);
t = peek ();
if (t && t->type == tok_operator &&
(t->content == "+" || t->content == "-"))
{
if (s->sort_direction)
throw PARSE_ERROR (_("multiple sort directives"));
s->sort_direction = (t->content == "+") ? 1 : -1;
s->sort_column = s->indexes.size();
swallow ();
}
if (parenthesized)
{
t = peek ();
if (t && t->type == tok_operator && t->content == ",")
{
swallow ();
continue;
}
else if (t && t->type == tok_operator && t->content == "]")
{
swallow ();
break;
}
else
throw PARSE_ERROR (_("expected ',' or ']'"));
}
else
break; }
t = next ();
if (! (t->type == tok_keyword && t->content == "in"))
throw PARSE_ERROR (_("expected 'in'"));
swallow ();
s->base = parse_indexable();
t = peek();
if (t && t->type == tok_operator && t->content == "[")
{
swallow();
while (1)
{
t = peek();
if (t && t->type == tok_operator && t->content == "*")
{
swallow();
s->array_slice.push_back (NULL);
}
else
s->array_slice.push_back (parse_expression());
t = peek ();
if (t && t->type == tok_operator && t->content == ",")
{
swallow ();
continue;
}
else if (t && t->type == tok_operator && t->content == "]")
{
swallow ();
break;
}
else
throw PARSE_ERROR (_("expected ',' or ']'"));
}
}
t = peek ();
if (t && t->type == tok_operator && t->content[0] == '@')
{
if (t->content == "@avg") s->sort_aggr = sc_average;
else if (t->content == "@min") s->sort_aggr = sc_min;
else if (t->content == "@max") s->sort_aggr = sc_max;
else if (t->content == "@count") s->sort_aggr = sc_count;
else if (t->content == "@sum") s->sort_aggr = sc_sum;
else throw PARSE_ERROR(_("expected statistical operation"));
swallow();
t = peek ();
if (! (t && t->type == tok_operator && (t->content == "+" || t->content == "-")))
throw PARSE_ERROR(_("expected sort directive"));
}
t = peek ();
if (t && t->type == tok_operator &&
(t->content == "+" || t->content == "-"))
{
if (s->sort_direction)
throw PARSE_ERROR (_("multiple sort directives"));
s->sort_direction = (t->content == "+") ? 1 : -1;
s->sort_column = 0;
swallow ();
}
t = peek ();
if (tok_is(t, tok_keyword, "limit"))
{
swallow (); s->limit = parse_expression ();
}
t = next ();
if (! (t->type == tok_operator && t->content == ")"))
throw PARSE_ERROR ("expected ')'");
swallow ();
s->block = parse_statement ();
return s;
}
expression*
parser::parse_expression ()
{
return parse_assignment ();
}
expression*
parser::parse_assignment ()
{
expression* op1 = parse_ternary ();
const token* t = peek ();
if (t && t->type == tok_operator
&& (t->content == "=" ||
t->content == "<<<" ||
t->content == "+=" ||
t->content == "-=" ||
t->content == "*=" ||
t->content == "/=" ||
t->content == "%=" ||
t->content == "<<=" ||
t->content == ">>=" ||
t->content == "&=" ||
t->content == "^=" ||
t->content == "|=" ||
t->content == ".=" ||
false))
{
assignment* e = new assignment;
e->left = op1;
e->op = t->content;
e->tok = t;
next ();
e->right = parse_expression ();
op1 = e;
}
return op1;
}
expression*
parser::parse_ternary ()
{
expression* op1 = parse_logical_or ();
const token* t = peek ();
if (t && t->type == tok_operator && t->content == "?")
{
ternary_expression* e = new ternary_expression;
e->tok = t;
e->cond = op1;
next ();
e->truevalue = parse_expression (); XXX
t = next ();
if (! (t->type == tok_operator && t->content == ":"))
throw PARSE_ERROR (_("expected ':'"));
swallow ();
e->falsevalue = parse_expression (); XXX
return e;
}
else
return op1;
}
expression*
parser::parse_logical_or ()
{
expression* op1 = parse_logical_and ();
const token* t = peek ();
while (t && t->type == tok_operator && t->content == "||")
{
logical_or_expr* e = new logical_or_expr;
e->tok = t;
e->op = t->content;
e->left = op1;
next ();
e->right = parse_logical_and ();
op1 = e;
t = peek ();
}
return op1;
}
expression*
parser::parse_logical_and ()
{
expression* op1 = parse_boolean_or ();
const token* t = peek ();
while (t && t->type == tok_operator && t->content == "&&")
{
logical_and_expr *e = new logical_and_expr;
e->left = op1;
e->op = t->content;
e->tok = t;
next ();
e->right = parse_boolean_or ();
op1 = e;
t = peek ();
}
return op1;
}
expression*
parser::parse_boolean_or ()
{
expression* op1 = parse_boolean_xor ();
const token* t = peek ();
while (t && t->type == tok_operator && t->content == "|")
{
binary_expression* e = new binary_expression;
e->left = op1;
e->op = t->content;
e->tok = t;
next ();
e->right = parse_boolean_xor ();
op1 = e;
t = peek ();
}
return op1;
}
expression*
parser::parse_boolean_xor ()
{
expression* op1 = parse_boolean_and ();
const token* t = peek ();
while (t && t->type == tok_operator && t->content == "^")
{
binary_expression* e = new binary_expression;
e->left = op1;
e->op = t->content;
e->tok = t;
next ();
e->right = parse_boolean_and ();
op1 = e;
t = peek ();
}
return op1;
}
expression*
parser::parse_boolean_and ()
{
expression* op1 = parse_array_in ();
const token* t = peek ();
while (t && t->type == tok_operator && t->content == "&")
{
binary_expression* e = new binary_expression;
e->left = op1;
e->op = t->content;
e->tok = t;
next ();
e->right = parse_array_in ();
op1 = e;
t = peek ();
}
return op1;
}
expression*
parser::parse_array_in ()
{
vector<expression*> indexes;
bool parenthesized = false;
const token* t = peek ();
if (t && t->type == tok_operator && t->content == "[")
{
swallow ();
parenthesized = true;
}
while (1)
{
t = peek();
if (t && t->type == tok_operator && t->content == "*" && parenthesized)
{
swallow();
indexes.push_back(NULL);
}
else
{
expression* op1 = parse_comparison_or_regex_query ();
indexes.push_back (op1);
}
if (parenthesized)
{
const token* t = peek ();
if (t && t->type == tok_operator && t->content == ",")
{
swallow ();
continue;
}
else if (t && t->type == tok_operator && t->content == "]")
{
swallow ();
break;
}
else
throw PARSE_ERROR (_("expected ',' or ']'"));
}
else
break; }
t = peek ();
if (t && t->type == tok_keyword && t->content == "in")
{
array_in *e = new array_in;
e->tok = t;
next ();
arrayindex* a = new arrayindex;
a->indexes = indexes;
a->base = parse_indexable();
a->tok = a->base->tok;
e->operand = a;
return e;
}
else if (indexes.size() == 1) return indexes[0];
else
throw PARSE_ERROR (_("unexpected comma-separated expression list"));
}
expression*
parser::parse_comparison_or_regex_query ()
{
expression* op1 = parse_shift ();
XXX const token *t = peek();
if (t && t->type == tok_operator
&& (t->content == "=~" ||
t->content == "!~"))
{
regex_query* r = new regex_query;
r->left = op1;
r->op = t->content;
r->tok = t;
next ();
r->right = parse_literal_string();
op1 = r;
t = peek ();
}
else while (t && t->type == tok_operator
&& (t->content == ">" ||
t->content == "<" ||
t->content == "==" ||
t->content == "!=" ||
t->content == "<=" ||
t->content == ">="))
{
comparison* e = new comparison;
e->left = op1;
e->op = t->content;
e->tok = t;
next ();
e->right = parse_shift ();
op1 = e;
t = peek ();
}
return op1;
}
expression*
parser::parse_shift ()
{
expression* op1 = parse_concatenation ();
const token* t = peek ();
while (t && t->type == tok_operator &&
(t->content == "<<" || t->content == ">>"))
{
binary_expression* e = new binary_expression;
e->left = op1;
e->op = t->content;
e->tok = t;
next ();
e->right = parse_concatenation ();
op1 = e;
t = peek ();
}
return op1;
}
expression*
parser::parse_concatenation ()
{
expression* op1 = parse_additive ();
const token* t = peek ();
XXX while (t && t->type == tok_operator && t->content == ".")
{
concatenation* e = new concatenation;
e->left = op1;
e->op = t->content;
e->tok = t;
next ();
e->right = parse_additive ();
op1 = e;
t = peek ();
}
return op1;
}
expression*
parser::parse_additive ()
{
expression* op1 = parse_multiplicative ();
const token* t = peek ();
while (t && t->type == tok_operator
&& (t->content == "+" || t->content == "-"))
{
binary_expression* e = new binary_expression;
e->op = t->content;
e->left = op1;
e->tok = t;
next ();
e->right = parse_multiplicative ();
op1 = e;
t = peek ();
}
return op1;
}
expression*
parser::parse_multiplicative ()
{
expression* op1 = parse_unary ();
const token* t = peek ();
while (t && t->type == tok_operator
&& (t->content == "*" || t->content == "/" || t->content == "%"))
{
binary_expression* e = new binary_expression;
e->op = t->content;
e->left = op1;
e->tok = t;
next ();
e->right = parse_unary ();
op1 = e;
t = peek ();
}
return op1;
}
expression*
parser::parse_unary ()
{
const token* t = peek ();
if (t && t->type == tok_operator
&& (t->content == "+" ||
t->content == "-" ||
t->content == "!" ||
t->content == "~" ||
false))
{
unary_expression* e = new unary_expression;
e->op = t->content;
e->tok = t;
next ();
e->operand = parse_unary ();
return e;
}
else
return parse_crement ();
}
expression*
parser::parse_crement () {
const token* t = peek ();
if (t && t->type == tok_operator
&& (t->content == "++" || t->content == "--"))
{
pre_crement* e = new pre_crement;
e->op = t->content;
e->tok = t;
next ();
e->operand = parse_dwarf_value ();
return e;
}
expression *op1 = parse_dwarf_value ();
t = peek ();
if (t && t->type == tok_operator
&& (t->content == "++" || t->content == "--"))
{
post_crement* e = new post_crement;
e->op = t->content;
e->tok = t;
next ();
e->operand = op1;
return e;
}
else
return op1;
}
expression*
parser::parse_dwarf_value ()
{
expression* expr = NULL;
target_symbol* tsym = NULL;
const token* addrtok = peek_op ("&") ? next () : NULL;
bool addressof = (addrtok != NULL);
const token* t = peek ();
if (t && t->type == tok_identifier && t->content[0] == '$')
expr = tsym = parse_target_symbol ();
else if (tok_is (t, tok_operator, "@cast"))
expr = tsym = parse_cast_op ();
else if (tok_is (t, tok_operator, "@var"))
expr = tsym = parse_atvar_op ();
else if (addressof && !input.has_version("2.6"))
throw PARSE_ERROR (_("expected @cast, @var or $var"));
else
expr = parse_value ();
if (!tsym && (addressof || peek_target_symbol_components ())
&& input.has_version("2.6"))
{
autocast_op *cop = new autocast_op;
cop->tok = addrtok ?: peek ();
cop->operand = expr;
expr = tsym = cop;
}
if (tsym)
{
tsym->addressof = addressof;
parse_target_symbol_components (tsym);
}
return expr;
}
expression*
parser::parse_value ()
{
const token* t = peek ();
if (! t)
throw PARSE_ERROR (_("expected value"));
if (t->type == tok_embedded)
{
if (! privileged)
throw PARSE_ERROR (_("embedded expression code in unprivileged script; need stap -g"), false);
embedded_expr *e = new embedded_expr;
e->tok = t;
e->code = t->content;
next ();
return e;
}
if (t->type == tok_operator && t->content == "(")
{
swallow ();
expression* e = parse_expression ();
t = next ();
if (! (t->type == tok_operator && t->content == ")"))
throw PARSE_ERROR (_("expected ')'"));
swallow ();
return e;
}
else if (t->type == tok_identifier
|| (t->type == tok_operator && t->content[0] == '@'))
return parse_symbol ();
else
return parse_literal ();
}
const token *
parser::parse_hist_op_or_bare_name (hist_op *&hop, string &name)
{
hop = NULL;
const token* t = expect_ident_or_atword (name);
if (name == "@hist_linear" || name == "@hist_log")
{
hop = new hist_op;
if (name == "@hist_linear")
hop->htype = hist_linear;
else if (name == "@hist_log")
hop->htype = hist_log;
hop->tok = t;
expect_op("(");
hop->stat = parse_expression ();
int64_t tnum;
if (hop->htype == hist_linear)
{
for (size_t i = 0; i < 3; ++i)
{
expect_op (",");
expect_number (tnum);
hop->params.push_back (tnum);
}
}
expect_op(")");
}
return t;
}
indexable*
parser::parse_indexable ()
{
hist_op *hop = NULL;
string name;
const token *tok = parse_hist_op_or_bare_name(hop, name);
if (hop)
return hop;
else
{
symbol* sym = new symbol;
sym->name = name;
sym->tok = tok;
return sym;
}
}
expression* parser::parse_symbol ()
{
hist_op *hop = NULL;
symbol *sym = NULL;
string name;
const token *t = parse_hist_op_or_bare_name(hop, name);
if (!hop)
{
if (name == "@defined")
return parse_defined_op (t);
if (name == "@entry")
return parse_entry_op (t);
if (name == "@perf")
return parse_perf_op (t);
if (name.size() > 0 && name[0] == '@')
{
stat_op *sop = new stat_op;
if (name == "@avg")
sop->ctype = sc_average;
else if (name == "@count")
sop->ctype = sc_count;
else if (name == "@sum")
sop->ctype = sc_sum;
else if (name == "@min")
sop->ctype = sc_min;
else if (name == "@max")
sop->ctype = sc_max;
else
throw PARSE_ERROR(_("unknown operator ") + name);
expect_op("(");
sop->tok = t;
sop->stat = parse_expression ();
expect_op(")");
return sop;
}
else if (print_format *fmt = print_format::create(t))
{
expect_op("(");
if ((name == "print" || name == "println" ||
name == "sprint" || name == "sprintln") &&
(peek_op("@hist_linear") || peek_op("@hist_log")))
{
hop = NULL;
t = parse_hist_op_or_bare_name(hop, name);
assert(hop);
XXX
if (!peek_op ("["))
fmt->hist = hop;
else
{
expect_op("[");
struct arrayindex* ai = new arrayindex;
ai->tok = t;
ai->base = hop;
ai->indexes.push_back (parse_expression ());
expect_op("]");
fmt->args.push_back(ai);
while (!peek_op (")"))
{
expect_op(",");
expression *e = parse_expression ();
fmt->args.push_back(e);
}
}
}
else
{
int min_args = 0;
bool consumed_arg = false;
if (fmt->print_with_format)
{
fmt->raw_components = parse_literal_string()->value;
fmt->components = print_format::string_to_components (fmt->raw_components);
consumed_arg = true;
}
else if (fmt->print_with_delim)
{
fmt->delimiter.clear();
fmt->delimiter.type = print_format::conv_literal;
fmt->delimiter.literal_string = parse_literal_string()->value;
consumed_arg = true;
min_args = 2; }
else if (!fmt->print_with_newline)
{
min_args = 1;
}
while (min_args || !peek_op (")"))
{
if (consumed_arg)
expect_op(",");
expression *e = parse_expression ();
fmt->args.push_back(e);
consumed_arg = true;
if (min_args)
--min_args;
}
}
expect_op(")");
return fmt;
}
else if (peek_op ("(")) {
swallow ();
struct functioncall* f = new functioncall;
f->tok = t;
f->function = name;
if (peek_op (")"))
{
swallow ();
return f;
}
while (1)
{
f->args.push_back (parse_expression ());
if (peek_op (")"))
{
swallow ();
break;
}
else if (peek_op (","))
{
swallow ();
continue;
}
else
throw PARSE_ERROR (_("expected ',' or ')'"));
}
return f;
}
else
{
sym = new symbol;
sym->name = name;
sym->tok = t;
}
}
assert (!hop != !sym);
if (peek_op ("[")) {
swallow ();
struct arrayindex* ai = new arrayindex;
ai->tok = t;
if (hop)
ai->base = hop;
else
ai->base = sym;
while (1)
{
if (peek_op("*"))
{
swallow();
ai->indexes.push_back (NULL);
}
else
ai->indexes.push_back (parse_expression ());
if (peek_op ("]"))
{
swallow ();
break;
}
else if (peek_op (","))
{
swallow ();
continue;
}
else
throw PARSE_ERROR (_("expected ',' or ']'"));
}
return ai;
}
if (hop)
throw PARSE_ERROR(_("base histogram operator where expression expected"), t);
return sym;
}
target_symbol* parser::parse_target_symbol ()
{
const token* t = next ();
if (t->type == tok_identifier && t->content[0]=='$')
{
target_symbol *tsym = new target_symbol;
tsym->tok = t;
tsym->name = t->content;
return tsym;
}
throw PARSE_ERROR (_("expected $var"));
}
cast_op* parser::parse_cast_op ()
{
const token* t = next ();
if (t->type == tok_operator && t->content == "@cast")
{
cast_op *cop = new cast_op;
cop->tok = t;
cop->name = t->content;
expect_op("(");
cop->operand = parse_expression ();
expect_op(",");
expect_unknown(tok_string, cop->type_name);
if (cop->type_name.empty())
throw PARSE_ERROR (_("expected non-empty string"));
if (peek_op (","))
{
swallow ();
expect_unknown(tok_string, cop->module);
}
expect_op(")");
return cop;
}
throw PARSE_ERROR (_("expected @cast"));
}
atvar_op* parser::parse_atvar_op ()
{
const token* t = next ();
if (t->type == tok_operator && t->content == "@var")
{
atvar_op *aop = new atvar_op;
aop->tok = t;
aop->name = t->content;
expect_op("(");
expect_unknown(tok_string, aop->target_name);
size_t found_at = aop->target_name.find("@");
if (found_at != string::npos)
aop->cu_name = aop->target_name.substr(found_at + 1);
else
aop->cu_name = "";
if (peek_op (","))
{
swallow ();
expect_unknown (tok_string, aop->module);
}
else
aop->module = "";
expect_op(")");
return aop;
}
throw PARSE_ERROR (_("expected @var"));
}
expression* parser::parse_defined_op (const token* t)
{
defined_op* dop = new defined_op;
dop->tok = t;
expect_op("(");
dop->operand = parse_expression ();
expect_op(")");
return dop;
}
expression* parser::parse_entry_op (const token* t)
{
entry_op* eop = new entry_op;
eop->tok = t;
expect_op("(");
eop->operand = parse_expression ();
expect_op(")");
return eop;
}
expression* parser::parse_perf_op (const token* t)
{
perf_op* pop = new perf_op;
pop->tok = t;
expect_op("(");
pop->operand = parse_literal_string ();
if (pop->operand->value == "")
throw PARSE_ERROR (_("expected non-empty string"));
expect_op(")");
return pop;
}
bool
parser::peek_target_symbol_components ()
{
const token * t = peek ();
return t &&
((t->type == tok_operator && (t->content == "->" || t->content == "["))
|| (t->type == tok_identifier &&
t->content.find_first_not_of('$') == string::npos));
}
void
parser::parse_target_symbol_components (target_symbol* e)
{
bool pprint = false;
string &base = e->name;
size_t pprint_pos = base.find_last_not_of('$');
if (0 < pprint_pos && pprint_pos < base.length() - 1)
{
string pprint_val = base.substr(pprint_pos + 1);
base.erase(pprint_pos + 1);
e->components.push_back (target_symbol::component(e->tok, pprint_val, true));
pprint = true;
}
while (!pprint)
{
if (peek_op ("->"))
{
const token* t = next();
string member;
expect_ident_or_keyword (member);
pprint_pos = member.find_last_not_of('$');
string pprint_val;
if (pprint_pos == string::npos || pprint_pos < member.length() - 1)
{
pprint_val = member.substr(pprint_pos + 1);
member.erase(pprint_pos + 1);
pprint = true;
}
if (!member.empty())
e->components.push_back (target_symbol::component(t, member));
if (pprint)
e->components.push_back (target_symbol::component(t, pprint_val, true));
}
else if (peek_op ("["))
{
const token* t = next();
expression* index = parse_expression();
literal_number* ln = dynamic_cast<literal_number*>(index);
if (ln)
e->components.push_back (target_symbol::component(t, ln->value));
else
e->components.push_back (target_symbol::component(t, index));
expect_op ("]");
}
else
break;
}
if (!pprint)
{
const token* t = peek();
if (t != NULL && t->type == tok_identifier &&
t->content.find_first_not_of('$') == string::npos)
{
t = next();
e->components.push_back (target_symbol::component(t, t->content, true));
pprint = true;
}
}
if (pprint && (peek_op ("->") || peek_op("[")))
throw PARSE_ERROR(_("-> and [ are not accepted for a pretty-printing variable"));
}