summaryrefslogtreecommitdiff
path: root/src/sp_config_scanner.re
diff options
context:
space:
mode:
authorBen Fuhrmannek2021-09-01 17:01:00 +0200
committerBen Fuhrmannek2021-09-01 17:01:00 +0200
commit782336d8f4b9c411d344c0e7752e87631952f268 (patch)
treedeba1aed1db8b64a6b2f5a1076914fe8743e6c14 /src/sp_config_scanner.re
parent5cdfa697666c0ed347aaa086953b7e9c7f8aa9f9 (diff)
implemented proper operand precedence using the shunting yard algorithm
Diffstat (limited to 'src/sp_config_scanner.re')
-rw-r--r--src/sp_config_scanner.re118
1 files changed, 96 insertions, 22 deletions
diff --git a/src/sp_config_scanner.re b/src/sp_config_scanner.re
index b36423b..f911df3 100644
--- a/src/sp_config_scanner.re
+++ b/src/sp_config_scanner.re
@@ -71,20 +71,60 @@ static void str_dtor(zval *zv) {
71 zend_string_release_ex(Z_STR_P(zv), 1); 71 zend_string_release_ex(Z_STR_P(zv), 1);
72} 72}
73 73
74static int apply_op(int v1, char op, int v2, int neg) { 74// sy_ functions and macros are helpers for the shunting yard algorithm
75 if (neg) { v2 = !v2; } 75#define sy_res_push(val) \
76 if (cond_res_i >= 100) { cs_error_log("condition too complex on line %d", lineno); goto out; } \
77 cond_res[cond_res_i++] = val;
78#define sy_res_pop() cond_res[--cond_res_i]
79#define sy_op_push(op) \
80 if (cond_op_i >= 100) { cs_error_log("condition too complex on line %d", lineno); goto out; } \
81 cond_op[cond_op_i++] = op;
82#define sy_op_pop() cond_op[--cond_op_i]
83#define sy_op_peek() cond_op[cond_op_i-1]
84
85static inline int sy_op_precedence(char op) {
76 switch (op) { 86 switch (op) {
77 case 0: return v2; 87 case '!': return 120;
78 case '&': return (v1 && v2); 88 case '<':
79 case '|': return (v1 || v2); 89 case '>':
80 case '<': return (v1 < v2); 90 case 'L': // <=
81 case '>': return (v1 > v2); 91 case 'G': // >=
82 case '=': return (v1 == v2); 92 return 90;
93 case '&': return 70;
94 case '|': return 60;
95 case '=': return 20;
83 } 96 }
84 return 0; 97 return 0;
85} 98}
99static inline int sy_op_is_left_assoc(char op) {
100 switch (op) {
101 case '!': return 0;
102 }
103 return 1;
104}
105static int sy_apply_op(char op, int a, int b) {
106 switch (op) {
107 case '!': return !a;
108 case '&': return (b && a);
109 case '|': return (b || a);
110 case '<': return (b < a);
111 case 'L': return (b <= a);
112 case 'G': return (b >= a);
113 case '>': return (b > a);
114 case '=': return (b == a);
115 }
116 return 0;
117}
118
119#define SY_APPLY_OP_FROM_STACK() \
120 char op = sy_op_pop(); \
121 int unary = (op == '!'); \
122 if (cond_res_i < (2 - unary)) { cs_error_log("not enough input on line %d", lineno); goto out; } \
123 int a = sy_res_pop(); \
124 int b = unary ? 0 : sy_res_pop(); \
125 int res = sy_apply_op(op, a, b); \
126 sy_res_push(res);
86 127
87#define APPLY_OP(v2) cond_res = apply_op(cond_res, cond_op, v2, cond_neg); cond_op = cond_neg = 0;
88 128
89zend_result sp_config_scan(char *data, zend_result (*process_rule)(sp_parsed_keyword*)) 129zend_result sp_config_scan(char *data, zend_result (*process_rule)(sp_parsed_keyword*))
90{ 130{
@@ -100,10 +140,13 @@ zend_result sp_config_scan(char *data, zend_result (*process_rule)(sp_parsed_key
100 140
101 HashTable vars; 141 HashTable vars;
102 zend_hash_init(&vars, 10, NULL, str_dtor, 1); 142 zend_hash_init(&vars, 10, NULL, str_dtor, 1);
143 zend_hash_str_add_ptr(&vars, ZEND_STRL("PHP_VERSION_ID"), zend_string_init(ZEND_STRL(ZEND_TOSTR(PHP_VERSION_ID)), 1));
103 144
104 int cond_res = 1; 145
105 char cond_op = 0; 146 int cond_res[100] = {0};
106 int cond_neg = 0; 147 int cond_res_i = 0;
148 char cond_op[100] = {0};
149 int cond_op_i = 0;
107 150
108 int cond = yycinit; 151 int cond = yycinit;
109 long lineno = 1; 152 long lineno = 1;
@@ -141,14 +184,14 @@ zend_result sp_config_scan(char *data, zend_result (*process_rule)(sp_parsed_key
141 goto yyc_init; 184 goto yyc_init;
142 } 185 }
143 <init> "@condition" ws+ { goto yyc_cond; } 186 <init> "@condition" ws+ { goto yyc_cond; }
144 <init> "@end_condition" ws* ";" { cond_res = 1; goto yyc_init; } 187 <init> "@end_condition" ws* ";" { cond_res[0] = 1; goto yyc_init; }
145 188
146 <cond> ws+ { goto yyc_cond; } 189 <cond> ws+ { goto yyc_cond; }
147 <cond> nl { lineno++; goto yyc_cond; } 190 <cond> nl { lineno++; goto yyc_cond; }
148 <cond> @t1 keyword @t2 "(" @t3 string? @t4 ")" { 191 <cond> @t1 keyword @t2 "(" @t3 string? @t4 ")" {
149 if (t4-t3 >= 2 && strlen("extension_loaded") == t2-t1 && strncmp("extension_loaded", t1, t2-t1) == 0) { 192 if (t4-t3 >= 2 && strlen("extension_loaded") == t2-t1 && strncmp("extension_loaded", t1, t2-t1) == 0) {
150 int is_loaded = (zend_hash_str_find_ptr(&module_registry, t3+1, t4-t3-2) != NULL); 193 int is_loaded = (zend_hash_str_find_ptr(&module_registry, t3+1, t4-t3-2) != NULL);
151 APPLY_OP(is_loaded); 194 sy_res_push(is_loaded);
152 } else { 195 } else {
153 cs_error_log("unknown function in condition on line %d", lineno); 196 cs_error_log("unknown function in condition on line %d", lineno);
154 goto out; 197 goto out;
@@ -161,21 +204,52 @@ zend_result sp_config_scan(char *data, zend_result (*process_rule)(sp_parsed_key
161 cs_error_log("unknown variable in condition on line %d", lineno); 204 cs_error_log("unknown variable in condition on line %d", lineno);
162 goto out; 205 goto out;
163 } 206 }
164 APPLY_OP(atoi(ZSTR_VAL(tmp))); 207 sy_res_push(atoi(ZSTR_VAL(tmp)));
165 goto yyc_cond_op; 208 goto yyc_cond_op;
166 } 209 }
167 <cond> @t1 [0-9]+ @t2 { APPLY_OP(atoi(t1)); goto yyc_cond_op; } 210 <cond> @t1 [0-9]+ @t2 { sy_res_push(atoi(t1)); goto yyc_cond_op; }
168 <cond> @t1 "!" { cond_neg = (cond_neg + 1) % 2; goto yyc_cond; } 211 <cond> @t1 "!" { sy_op_push(*t1); goto yyc_cond; }
212 <cond> @t1 "(" { sy_op_push(*t1); goto yyc_cond; }
169 <cond_op> ws+ { goto yyc_cond_op; } 213 <cond_op> ws+ { goto yyc_cond_op; }
170 <cond_op> nl { lineno++; goto yyc_cond_op; } 214 <cond_op> nl { lineno++; goto yyc_cond_op; }
171 <cond_op> @t1 ( "&&" | "||" | "<" | ">" | "==" ) { cond_op = *t1; goto yyc_cond; } 215 <cond_op> @t1 ( "&&" | "||" | "<" | ">" | "==" | "<=" | ">=") @t2 {
172 <cond_op> ";" { goto yyc_init; } 216 char op1 = *t1;
173 <cond, cond_op> * { cs_error_log("Syntax error in condition on line %d", lineno); return false; } 217 if (t2-t1 == 2) {
218 switch (op1) {
219 case '<': op1 = 'L'; break; // <=
220 case '>': op1 = 'G'; break; // >=
221 }
222 }
223 while (cond_op_i && sy_op_peek() != '(' && ((sy_op_precedence(sy_op_peek()) > sy_op_precedence(*t1)) || (sy_op_precedence(sy_op_peek()) == sy_op_precedence(*t1)) && sy_op_is_left_assoc(*t1))) {
224 SY_APPLY_OP_FROM_STACK();
225 }
226 sy_op_push(*t1);
227 goto yyc_cond;
228 }
229 <cond_op> ")" {
230 while (cond_op_i && sy_op_peek() != '(') {
231 SY_APPLY_OP_FROM_STACK();
232 }
233 if (cond_op_i == 0 || sy_op_peek() != '(') {
234 cs_error_log("unbalanced parathesis on line %d", lineno); goto out;
235 }
236 sy_op_pop();
237 goto yyc_cond_op;
238 }
239 <cond_op> ";" {
240 while (cond_op_i) {
241 if (sy_op_peek() == '(') { cs_error_log("unbalanced parathesis on line %d", lineno); goto out; }
242 SY_APPLY_OP_FROM_STACK();
243 }
244 if (cond_res_i > 1) { cs_error_log("invalid condition on line %d", lineno); goto out; }
245 goto yyc_init;
246 }
247 <cond, cond_op> * { cs_error_log("Syntax error in condition on line %d", lineno); goto out; }
174 248
175 <rule> ws+ { goto yyc_rule; } 249 <rule> ws+ { goto yyc_rule; }
176 <rule> nl / ( nl | ws )* "." { lineno++; goto yyc_rule; } 250 <rule> nl / ( nl | ws )* "." { lineno++; goto yyc_rule; }
177 <rule> "." @t1 keyword @t2 ( "(" @t3 ( string? | keyword ) @t4 ")" )? { 251 <rule> "." @t1 keyword @t2 ( "(" @t3 ( string? | keyword ) @t4 ")" )? {
178 if (!cond_res) { goto yyc_rule; } 252 if (!cond_res[0]) { goto yyc_rule; }
179 if (kw_i == max_keywords) { 253 if (kw_i == max_keywords) {
180 cs_error_log("Too many keywords in rule (more than %d) on line %d", max_keywords, lineno); 254 cs_error_log("Too many keywords in rule (more than %d) on line %d", max_keywords, lineno);
181 goto out; 255 goto out;
@@ -206,7 +280,7 @@ zend_result sp_config_scan(char *data, zend_result (*process_rule)(sp_parsed_key
206 } 280 }
207 <rule> ";" { 281 <rule> ";" {
208 end_of_rule: 282 end_of_rule:
209 if (!cond_res) { goto yyc_init; } 283 if (!cond_res[0]) { goto yyc_init; }
210 parsed_rule[kw_i++] = (sp_parsed_keyword){0, 0, 0, 0, 0, 0}; 284 parsed_rule[kw_i++] = (sp_parsed_keyword){0, 0, 0, 0, 0, 0};
211 if (process_rule && process_rule(parsed_rule) != SUCCESS) { 285 if (process_rule && process_rule(parsed_rule) != SUCCESS) {
212 goto out; 286 goto out;