This is the mail archive of the
systemtap@sourceware.org
mailing list for the systemtap project.
[PATCH] Make the 3rd operand of ternary '?:' bind tighter than binary '='
- From: "Yichun Zhang (agentzh)" <agentzh at gmail dot com>
- To: systemtap at sourceware dot org
- Cc: "Yichun Zhang (agentzh)" <agentzh at gmail dot com>
- Date: Mon, 27 Aug 2018 16:05:35 -0700
- Subject: [PATCH] Make the 3rd operand of ternary '?:' bind tighter than binary '='
In the C language, the 3rd operand of the ternary operator binds tighter
than the binary assignment operators. It is better for the stap language
to be consistent with C in operator precedence.
Added several tests to check the precedence of the ternary operator.
I've verified the results with similar C programs with gcc myself.
---
parse.cxx | 2 +-
testsuite/systemtap.base/ternary_op.exp | 135 ++++++++++++++++++++++++++++++
testsuite/systemtap.base/ternary_op_1.stp | 5 ++
testsuite/systemtap.base/ternary_op_2.stp | 5 ++
testsuite/systemtap.base/ternary_op_3.stp | 5 ++
5 files changed, 151 insertions(+), 1 deletion(-)
create mode 100644 testsuite/systemtap.base/ternary_op.exp
create mode 100644 testsuite/systemtap.base/ternary_op_1.stp
create mode 100644 testsuite/systemtap.base/ternary_op_2.stp
create mode 100644 testsuite/systemtap.base/ternary_op_3.stp
diff --git a/parse.cxx b/parse.cxx
index 751f56add..169486d8d 100644
--- a/parse.cxx
+++ b/parse.cxx
@@ -3286,7 +3286,7 @@ parser::parse_ternary ()
throw PARSE_ERROR (_("expected ':'"));
swallow ();
- e->falsevalue = parse_expression (); // XXX
+ e->falsevalue = parse_ternary (); // XXX
return e;
}
else
diff --git a/testsuite/systemtap.base/ternary_op.exp b/testsuite/systemtap.base/ternary_op.exp
new file mode 100644
index 000000000..4f9eabf21
--- /dev/null
+++ b/testsuite/systemtap.base/ternary_op.exp
@@ -0,0 +1,135 @@
+set test "ternary_op"
+set testpath "$srcdir/$subdir"
+
+if {! [installtest_p]} { untested "$test"; return }
+
+# --- TEST 1 ---
+
+set subtest1 "TEST 1: ternary operator should be tighter than ,"
+foreach runtime [get_runtime_list] {
+ if {$runtime eq ""} {
+ set runtime "kernel"
+ }
+ set test_name "$test: $subtest1 ($runtime)"
+
+ set cmd "stap --runtime=$runtime '$srcdir/$subdir/${test}_1.stp'"
+ send_log "executing: $cmd\n"
+ set pipe [open "| sh -c {$cmd}" r]
+ set out [read $pipe]
+ set failed 0
+ if {[catch {close $pipe} stderr] != 0} {
+ if {$stderr ne "" && [string index $stderr end] ne "\n"} {
+ append stderr "\n"
+ }
+ global errorCode
+ if {"CHILDSTATUS" == [lindex $errorCode 0]} {
+ set failed [lindex $errorCode 2]
+ }
+ }
+
+ set exp_out "positive 3\n"
+ regsub -all -- {\n} $exp_out {\n} escaped_exp_out
+ if {$out eq $exp_out} {
+ pass "${test_name}: stdout matches \"$escaped_exp_out\""
+ } else {
+ fail "${test_name}: stdout fails to match \"$escaped_exp_out\": got \"$out\""
+ }
+
+ if {$failed} {
+ fail "${test_name}: exit code should be zero but is $failed"
+ } else {
+ pass "${test_name}: exit code is zero"
+ }
+ if {$stderr ne ""} {
+ send_log "stderr:\n$stderr"
+ }
+}
+
+# --- TEST 2 ---
+
+set subtest2 "TEST 2: ternary operator should be tighter than = (bad case)"
+set test_name "$test: $subtest2"
+
+set cmd "stap '$srcdir/$subdir/${test}_2.stp'"
+send_log "executing: $cmd\n"
+set pipe [open "| sh -c {$cmd}" r]
+set out [read $pipe]
+set failed 0
+if {[catch {close $pipe} stderr] != 0} {
+ if {$stderr ne "" && [string index $stderr end] ne "\n"} {
+ append stderr "\n"
+ }
+ global errorCode
+ if {"CHILDSTATUS" == [lindex $errorCode 0]} {
+ set failed [lindex $errorCode 2]
+ }
+}
+
+set exp_out ""
+regsub -all -- {\n} $exp_out {\n} escaped_exp_out
+if {$out eq $exp_out} {
+ pass "${test_name}: stdout matches \"$escaped_exp_out\""
+} else {
+ fail "${test_name}: stdout fails to match \"$escaped_exp_out\": got \"$out\""
+}
+
+if {$failed} {
+ pass "${test_name}: exit code should be non-zero"
+} else {
+ fail "${test_name}: exit code should be non-zero but is zero"
+}
+
+set stderr_pat "^semantic error: Expecting lvalue for assignment: operator '=' at :3:38\\n source: printf\\(\"%d\\\\n\", a > 0 \\? b = 3 : c = 4\\);\\n \\^\\n"
+regsub -all -- {\n} $stderr_pat {\n} escaped_stderr_pat
+if {[regexp -linestop -lineanchor -- $stderr_pat $stderr]} {
+ pass "${test_name}: stderr matches \"$escaped_stderr_pat\""
+} else {
+ fail "${test_name}: stderr fails to match \"$escaped_stderr_pat\": got \"$stderr\""
+}
+
+# --- TEST 3 ---
+
+set subtest3 "TEST 3: ternary operator should be tighter than = (good case, with parens)"
+foreach runtime [get_runtime_list] {
+ if {$runtime eq ""} {
+ set runtime "kernel"
+ }
+ set test_name "$test: $subtest3 ($runtime)"
+
+ set cmd "stap --runtime=$runtime '$srcdir/$subdir/${test}_3.stp'"
+ send_log "executing: $cmd\n"
+ set pipe [open "| sh -c {$cmd}" r]
+ set out [read $pipe]
+ set failed 0
+ if {[catch {close $pipe} stderr] != 0} {
+ if {$stderr ne "" && [string index $stderr end] ne "\n"} {
+ append stderr "\n"
+ }
+ global errorCode
+ if {"CHILDSTATUS" == [lindex $errorCode 0]} {
+ set failed [lindex $errorCode 2]
+ }
+ }
+
+ set exp_out "3\n"
+ regsub -all -- {\n} $exp_out {\n} escaped_exp_out
+ if {$out eq $exp_out} {
+ pass "${test_name}: stdout matches \"$escaped_exp_out\""
+ } else {
+ fail "${test_name}: stdout fails to match \"$escaped_exp_out\": got \"$out\""
+ }
+
+ set stderr_pat "^WARNING: Eliding assignment to 'b': operator '=' at \[^\\n\]*?\\.stp:3:30\n.*?\nWARNING: Eliding assignment to 'c': operator '=' at :3:39\n"
+ regsub -all -- {\n} $stderr_pat {\n} escaped_stderr_pat
+ if {[regexp -lineanchor -- $stderr_pat $stderr]} {
+ pass "${test_name}: stderr matches \"$escaped_stderr_pat\""
+ } else {
+ fail "${test_name}: stderr fails to match \"$escaped_stderr_pat\": got \"$stderr\""
+ }
+
+ if {$failed} {
+ fail "${test_name}: exit code should be zero but is $failed"
+ } else {
+ pass "${test_name}: exit code is zero"
+ }
+}
diff --git a/testsuite/systemtap.base/ternary_op_1.stp b/testsuite/systemtap.base/ternary_op_1.stp
new file mode 100644
index 000000000..d4be95987
--- /dev/null
+++ b/testsuite/systemtap.base/ternary_op_1.stp
@@ -0,0 +1,5 @@
+probe begin {
+ a = 3;
+ printf("%s %d\n", a > 0 ? "positive" : "negative", a);
+ exit();
+}
diff --git a/testsuite/systemtap.base/ternary_op_2.stp b/testsuite/systemtap.base/ternary_op_2.stp
new file mode 100644
index 000000000..ca4426812
--- /dev/null
+++ b/testsuite/systemtap.base/ternary_op_2.stp
@@ -0,0 +1,5 @@
+probe begin {
+ a = 3;
+ printf("%d\n", a > 0 ? b = 3 : c = 4);
+ exit();
+}
diff --git a/testsuite/systemtap.base/ternary_op_3.stp b/testsuite/systemtap.base/ternary_op_3.stp
new file mode 100644
index 000000000..8e6593b28
--- /dev/null
+++ b/testsuite/systemtap.base/ternary_op_3.stp
@@ -0,0 +1,5 @@
+probe begin {
+ a = 3;
+ printf("%d\n", a > 0 ? b = 3 : (c = 4));
+ exit();
+}
--
2.11.0.295.gd7dffce