This is the mail archive of the systemtap@sourceware.org mailing list for the systemtap project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Patch: allow use of $x and @x


Ok, here is a patch including everything (hopefully) about the use of $# and @# to access the number of argument..

I've merged the two patches into one.

I have updated and added some tests in the testsuite to test these new identifiers. And I also have (tried to) updated the man.
Also there is some minor changes/updates to improve my previous code.


Here is a detailled changelog:

	* parse.cxx, parse.h: Add $# and @# identifiers to access the number
	of arguments.
	- Allow the use of $x and @x identifiers during the preprocessing.
	- Produce more accurate error message when having incomplete
	conditional during the preprocessing.
	* stap.1.in: Document $# and @# identifiers.

And in the testsuite:
	* 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.

Thanks,

Pierre Peiffer a écrit :
Frank Ch. Eigler a écrit :


The ideas and patches look fine. They would need to be completed by the addition of some documentation to the man pages, and some bits for the testsuite. Would you like to do that too?

Yep, I'll try to do that today.


-- Pierre
---
 ChangeLog                          |    9 ++
 parse.cxx                          |  128 +++++++++++++++++++++++++++----------
 parse.h                            |    5 -
 stap.1.in                          |   23 ++++--
 testsuite/ChangeLog                |    8 ++
 testsuite/parseko/preprocess10.stp |    4 +
 testsuite/parseko/preprocess11.stp |    4 +
 testsuite/parseko/preprocess12.stp |    5 +
 testsuite/parseok/fourteen.stp     |   11 +++
 9 files changed, 157 insertions(+), 40 deletions(-)

Index: b/parse.cxx
===================================================================
--- 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_sess
       
       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
         {
Index: b/ChangeLog
===================================================================
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2007-04-03  Pierre Peiffer  <pierre.peiffer@bull.net>
+
+	* parse.cxx, parse.h: Add $# and @# identifiers to access the number
+	of arguments.
+	- Allow the use of $x and @x identifiers during the preprocessing.
+	- Produce more accurate error message when having incomplete
+	conditional during the preprocessing.
+	* stap.1.in: Document $# and @# identifiers.
+
 2007-03-22  Frank Ch. Eigler  <fche@elastic.org>
 
 	PR 4224.
Index: b/parse.h
===================================================================
--- 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
@@ -64,7 +65,7 @@ struct systemtap_session;
 class lexer
 {
 public:
-  token* scan ();
+  token* scan (bool expand_args=true);
   lexer (std::istream&, const std::string&, systemtap_session&);
 
 private:
@@ -124,7 +125,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 ();
Index: b/stap.1.in
===================================================================
--- 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 s
 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
Index: b/testsuite/parseko/preprocess10.stp
===================================================================
--- /dev/null
+++ b/testsuite/parseko/preprocess10.stp
@@ -0,0 +1,4 @@
+#! stap -p1
+
+# expected number as right value in comparison
+%( $# != "2" %? probe begin { } %)
Index: b/testsuite/parseko/preprocess11.stp
===================================================================
--- /dev/null
+++ b/testsuite/parseko/preprocess11.stp
@@ -0,0 +1,4 @@
+#! stap -p1
+
+# expected string as right value in comparison
+%( @# != 2 %? probe begin { } %)
Index: b/testsuite/parseko/preprocess12.stp
===================================================================
--- /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 } %)
Index: b/testsuite/parseok/fourteen.stp
===================================================================
--- 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" %)
Index: b/testsuite/ChangeLog
===================================================================
--- 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-03-22  Frank Ch. Eigler  <fche@elastic.org>
 
 	PR 4224.

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]