summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfche <fche>2007-04-04 21:18:23 +0000
committerfche <fche>2007-04-04 21:18:23 +0000
commit5811366a62325d2cacf5509c25ee564ae3269f9b (patch)
tree655f7d1b88d1901b7121151951b671cd9c940105
parent3b5793932ec33f897433bc513b49f35bd8b35b2f (diff)
downloadsystemtap-steved-5811366a62325d2cacf5509c25ee564ae3269f9b.tar.gz
systemtap-steved-5811366a62325d2cacf5509c25ee564ae3269f9b.tar.xz
systemtap-steved-5811366a62325d2cacf5509c25ee564ae3269f9b.zip
2007-04-03 Pierre Peiffer <pierre.peiffer@bull.net>
* parse.cxx, parse.h (lexer::scan): Add $# and @# identifiers. (eval_pp_conditional, scan_pp): Allow the use of $x and @x identifiers. Produce more accurate error messages. * stap.1.in: Document $# and @# identifiers. 2007-04-04 Pierre Peiffer <pierre.peiffer@bull.net> * parseok/fourteen.stp: Add test about $# and @# usage during the preprocessing. * parseko/preprocess10.stp: New test. * parseko/preprocess11.stp: New test. * parseko/preprocess12.stp: New test.
-rw-r--r--ChangeLog7
-rw-r--r--parse.cxx128
-rw-r--r--parse.h5
-rw-r--r--stap.1.in23
-rw-r--r--testsuite/ChangeLog8
-rwxr-xr-xtestsuite/parseko/preprocess10.stp4
-rwxr-xr-xtestsuite/parseko/preprocess11.stp4
-rwxr-xr-xtestsuite/parseko/preprocess12.stp5
-rwxr-xr-xtestsuite/parseok/fourteen.stp11
9 files changed, 155 insertions, 40 deletions
diff --git a/ChangeLog b/ChangeLog
index ef96a87d..886d35bb 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2007-04-03 Pierre Peiffer <pierre.peiffer@bull.net>
+
+ * parse.cxx, parse.h (lexer::scan): Add $# and @# identifiers.
+ (eval_pp_conditional, scan_pp): Allow the use of $x and @x identifiers.
+ Produce more accurate error messages.
+ * stap.1.in: Document $# and @# identifiers.
+
2007-04-04 Frank Ch. Eigler <fche@elastic.org>
GCC 4.3 compatibility patches from Debian.
diff --git a/parse.cxx b/parse.cxx
index 74cb72d0..a960e249 100644
--- a/parse.cxx
+++ b/parse.cxx
@@ -1,6 +1,7 @@
// recursive descent parser for systemtap scripts
// Copyright (C) 2005-2007 Red Hat Inc.
// Copyright (C) 2006 Intel Corporation.
+// Copyright (C) 2007 Bull S.A.S
//
// This file is part of systemtap, and is free software. You can
// redistribute it and/or modify it under the terms of the GNU General
@@ -146,6 +147,8 @@ parser::last ()
// The basic form is %( CONDITION %? THEN-TOKENS %: ELSE-TOKENS %)
// where CONDITION is: kernel_v[r] COMPARISON-OP "version-string"
// or: arch COMPARISON-OP "arch-string"
+// or: "string1" COMPARISON-OP "string2"
+// or: number1 COMPARISON-OP number2
// The %: ELSE-TOKENS part is optional.
//
// e.g. %( kernel_v > "2.5" %? "foo" %: "baz" %)
@@ -212,14 +215,51 @@ 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))
+ {
+ // 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;
+
+ return (rvc_result == rvc_ok1 || rvc_result == rvc_ok2);
+ }
+ 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);
// XXX: support other forms? "CONFIG_SMP" ?
else
- throw parse_error ("expected 'arch' or 'kernel_v' or 'kernel_vr'", l);
+ 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 ()
+parser::scan_pp (bool expand_args)
{
while (true)
{
@@ -230,7 +270,7 @@ parser::scan_pp ()
return t;
}
- const token* t = input.scan (); // NB: not recursive!
+ const token* t = input.scan (expand_args); // NB: not recursive!
if (t == 0) // EOF
return t;
@@ -240,15 +280,17 @@ parser::scan_pp ()
// We have a %( - it's time to throw a preprocessing party!
const token *l, *op, *r;
- l = input.scan (); // NB: not recursive, though perhaps could be
- op = input.scan ();
- r = input.scan ();
+ l = input.scan (expand_args); // NB: not recursive, though perhaps could be
+ op = input.scan (expand_args);
+ r = input.scan (expand_args);
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.
- bool result = eval_pp_conditional (session, l, op, r);
+ // 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);
delete l;
delete op;
delete r;
@@ -259,13 +301,18 @@ parser::scan_pp ()
delete m; // "%?"
vector<const token*> my_enqueued_pp;
+ bool have_token = false;
while (true) // consume THEN tokens
{
- m = scan_pp (); // NB: recursive
+ m = scan_pp (result); // NB: recursive
if (m == 0)
- throw parse_error ("missing THEN tokens for conditional", t);
-
+ if (have_token)
+ throw parse_error ("incomplete conditional - missing %: or %)", t);
+ else
+ throw parse_error ("missing THEN tokens for conditional", t);
+
+ have_token = true;
if (m->type == tok_operator && (m->content == "%:" || // ELSE
m->content == "%)")) // END
break;
@@ -277,15 +324,20 @@ parser::scan_pp ()
// continue
}
+ have_token = false;
if (m && m->type == tok_operator && m->content == "%:") // ELSE
{
delete m; // "%:"
while (true)
{
- m = scan_pp (); // NB: recursive
+ m = scan_pp (expand_args && !result); // NB: recursive
if (m == 0)
- throw parse_error ("missing ELSE tokens for conditional", t);
+ if (have_token)
+ throw parse_error ("incomplete conditional - missing %)", t);
+ else
+ throw parse_error ("missing ELSE tokens for conditional", t);
+ have_token = true;
if (m->type == tok_operator && m->content == "%)") // END
break;
// enqueue token
@@ -473,7 +525,7 @@ lexer::input_get ()
token*
-lexer::scan ()
+lexer::scan (bool expand_args)
{
token* n = new token;
n->location.file = input_name;
@@ -501,7 +553,7 @@ lexer::scan ()
int c2 = input_peek ();
if (! input)
break;
- if ((isalnum(c2) || c2 == '_' || c2 == '$'))
+ if ((isalnum(c2) || c2 == '_' || c2 == '$' || c2 == '#' ))
{
n->content.push_back(c2);
input_get ();
@@ -514,24 +566,36 @@ lexer::scan ()
// numbers and @1 .. @999 as strings.
if (n->content[0] == '@' || n->content[0] == '$')
{
- string idxstr = n->content.substr(1);
- const char* startp = idxstr.c_str();
- char *endp;
- errno = 0;
- unsigned long idx = strtoul (startp, &endp, 10);
- if (endp == startp)
- ; // no numbers at all - leave alone as identifier
- else
- {
- // Use @1/$1 as the base, not @0/$0. Thus the idx-1.
- if (errno == ERANGE || errno == EINVAL || *endp != '\0' ||
- idx == 0 || idx-1 >= session.args.size ())
- throw parse_error ("command line argument index invalid or out of range", n);
-
- string arg = session.args[idx-1];
- n->type = (n->content[0] == '@') ? tok_string : tok_number;
- n->content = arg;
- }
+ if (!expand_args)
+ return n;
+ if (n->content[1] == '#')
+ {
+ stringstream converter;
+ converter << session.args.size ();
+ n->type = (n->content[0] == '@') ? tok_string : tok_number;
+ n->content = converter.str();
+ }
+ else
+ {
+ string idxstr = n->content.substr(1);
+ const char* startp = idxstr.c_str();
+ char *endp;
+ errno = 0;
+ unsigned long idx = strtoul (startp, &endp, 10);
+ if (endp == startp)
+ ; // no numbers at all - leave alone as identifier
+ else
+ {
+ // Use @1/$1 as the base, not @0/$0. Thus the idx-1.
+ if (errno == ERANGE || errno == EINVAL || *endp != '\0' ||
+ idx == 0 || idx-1 >= session.args.size ())
+ throw parse_error ("command line argument index invalid or out of range", n);
+
+ string arg = session.args[idx-1];
+ n->type = (n->content[0] == '@') ? tok_string : tok_number;
+ n->content = arg;
+ }
+ }
}
else
{
diff --git a/parse.h b/parse.h
index c6a778ca..b160b4f9 100644
--- a/parse.h
+++ b/parse.h
@@ -1,5 +1,6 @@
// -*- C++ -*-
// Copyright (C) 2005 Red Hat Inc.
+// Copyright (C) 2007 Bull S.A.S
//
// This file is part of systemtap, and is free software. You can
// redistribute it and/or modify it under the terms of the GNU General
@@ -68,7 +69,7 @@ struct systemtap_session;
class lexer
{
public:
- token* scan ();
+ token* scan (bool expand_args=true);
lexer (std::istream&, const std::string&, systemtap_session&);
private:
@@ -128,7 +129,7 @@ private:
// preprocessing subordinate
std::vector<const token*> enqueued_pp;
- const token* scan_pp ();
+ const token* scan_pp (bool expand_args=true);
// scanning state
const token* last ();
diff --git a/stap.1.in b/stap.1.in
index 6f31f711..0c968620 100644
--- a/stap.1.in
+++ b/stap.1.in
@@ -185,10 +185,16 @@ be expanded as literals. Use
.B $1 ... $<NN>
for casting as a numeric literal and
.B @1 ... @<NN>
-for casting as string literal. These may be used in all contexts
-where literals are accepted. Reference to an argument number beyond
-what was actually given is an error.
-.PP
+for casting as string literal. The number of arguments may be accessed
+through
+.B $#
+(as a numeric literal) or through
+.B @#
+(as a string literal). These may be used in all contexts where literals
+are accepted, including preprocessing stage. Reference to an argument
+number beyond what was actually given is an error.
+
+.SS PREPROCESSING
A simple conditional preprocessing stage is run as a part of parsing.
The general form is similar to the
.RB cond " ? " exp1 " : " exp2
@@ -197,8 +203,9 @@ ternary operator:
.BR %( " CONDITION " %? " TRUE-TOKENS " %)
.BR %( " CONDITION " %? " TRUE-TOKENS " %: " FALSE-TOKENS " %)
.ESAMPLE
-The CONDITION is a very limited expression whose format is determined
-by its first keyword.
+The CONDITION is either an expression whose format is determined by its
+first keyword, or a string literals comparison or a numeric literals
+comparison.
.PP
If the first part is the identifier
.BR kernel_vr " or " kernel_v
@@ -222,6 +229,10 @@ then the second part is one of the two string comparison operators
and the third part is a string literal for matching it. This
comparison is simple string (in)equality.
.PP
+Otherwise, the CONDITION is expected to be a comparison between two string
+literals or two numeric literals. In this case, the arguments are the only
+variables usable.
+.PP
The TRUE-TOKENS and FALSE-TOKENS are zero or more general parser
tokens (possibly including nested preprocessor conditionals), and are
pasted into the input stream if the condition is true or false. For
diff --git a/testsuite/ChangeLog b/testsuite/ChangeLog
index bf9bfae6..df1144c5 100644
--- a/testsuite/ChangeLog
+++ b/testsuite/ChangeLog
@@ -1,3 +1,11 @@
+2007-04-04 Pierre Peiffer <pierre.peiffer@bull.net>
+
+ * parseok/fourteen.stp: Add test about $# and @# usage during
+ the preprocessing.
+ * parseko/preprocess10.stp: New test.
+ * parseko/preprocess11.stp: New test.
+ * parseko/preprocess12.stp: New test.
+
2007-04-02 Frank Ch. Eigler <fche@elastic.org>
* systemtap.samples/poll_map.stp, profile.stp, syscalls.stp:
diff --git a/testsuite/parseko/preprocess10.stp b/testsuite/parseko/preprocess10.stp
new file mode 100755
index 00000000..5fcb7e9f
--- /dev/null
+++ b/testsuite/parseko/preprocess10.stp
@@ -0,0 +1,4 @@
+#! stap -p1
+
+# expected number as right value in comparison
+%( $# != "2" %? probe begin { } %)
diff --git a/testsuite/parseko/preprocess11.stp b/testsuite/parseko/preprocess11.stp
new file mode 100755
index 00000000..586aec1a
--- /dev/null
+++ b/testsuite/parseko/preprocess11.stp
@@ -0,0 +1,4 @@
+#! stap -p1
+
+# expected string as right value in comparison
+%( @# != 2 %? probe begin { } %)
diff --git a/testsuite/parseko/preprocess12.stp b/testsuite/parseko/preprocess12.stp
new file mode 100755
index 00000000..3ff3e34e
--- /dev/null
+++ b/testsuite/parseko/preprocess12.stp
@@ -0,0 +1,5 @@
+#! stap -p1
+
+# command line argument index invalid or out of range
+# (try to access to an unavailable argument)
+%( $# < 2 %? probe begin { print @1 } %)
diff --git a/testsuite/parseok/fourteen.stp b/testsuite/parseok/fourteen.stp
index cc51b90b..30ada648 100755
--- a/testsuite/parseok/fourteen.stp
+++ b/testsuite/parseok/fourteen.stp
@@ -15,3 +15,14 @@ global
%: %( arch != "x86_64" %? other %: x86_64 %)
%)
%)
+
+global
+%( $# != 2 %? /* and */
+ %( @# < "1" %? /* and */
+ %( @# == "0" %? /* and */
+ %( $# >= 3 %? /* and */
+ %( $2 >= "12" %? $3 FAIL5 %: $2 FAIL6 %) #This line must not be evaluated
+ %: PASS2 %)
+ %: "FAIL7" %)
+ %: "FAIL8" %)
+%: "FAIL9" %)