PageRenderTime 61ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/application/helpers/expressions/em_core_helper.php

https://bitbucket.org/machaven/limesurvey
PHP | 3533 lines | 2969 code | 130 blank | 434 comment | 460 complexity | 2623442eb3aa6827b4edec71caa1dc50 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, BSD-3-Clause, GPL-3.0, LGPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * LimeSurvey
  4. * Copyright (C) 2007-2011 The LimeSurvey Project Team / Carsten Schmitz
  5. * All rights reserved.
  6. * License: GNU/GPL License v2 or later, see LICENSE.php
  7. * LimeSurvey is free software. This version may have been modified pursuant
  8. * to the GNU General Public License, and as distributed it includes or
  9. * is derivative of works licensed under the GNU General Public License or
  10. * other free or open source software licenses.
  11. * See COPYRIGHT.php for copyright notices and details.
  12. *
  13. * $Id$
  14. */
  15. /**
  16. * Description of ExpressionManager
  17. * (1) Does safe evaluation of PHP expressions. Only registered Functions, and known Variables are allowed.
  18. * (a) Functions include any math, string processing, conditional, formatting, etc. functions
  19. * (2) This class replaces LimeSurvey's <= 1.91+ process of resolving strings that contain LimeReplacementFields
  20. * (a) String is split by expressions (by curly braces, but safely supporting strings and escaped curly braces)
  21. * (b) Expressions (things surrounded by curly braces) are evaluated - thereby doing LimeReplacementField substitution and/or more complex calculations
  22. * (c) Non-expressions are left intact
  23. * (d) The array of stringParts are re-joined to create the desired final string.
  24. * (3) The core of Expression Manager is a Recursive Descent Parser (RDP), based off of one build via JavaCC by TMSWhite in 1999.
  25. * (a) Functions that start with RDP_ should not be touched unless you really understand compiler design.
  26. *
  27. * @author Thomas M. White (TMSWhite)
  28. */
  29. class ExpressionManager {
  30. // These are the allowable suffixes for variables - each represents an attribute of a variable.
  31. static $RDP_regex_var_attr = 'code|gid|grelevance|gseq|jsName|mandatory|NAOK|qid|qseq|question|readWrite|relevanceStatus|relevance|rowdivid|sgqa|shown|type|valueNAOK|value';
  32. // These three variables are effectively static once constructed
  33. private $RDP_ExpressionRegex;
  34. private $RDP_TokenType;
  35. private $RDP_TokenizerRegex;
  36. private $RDP_CategorizeTokensRegex;
  37. private $RDP_ValidFunctions; // names and # params of valid functions
  38. // Thes variables are used while processing the equation
  39. private $RDP_expr; // the source expression
  40. private $RDP_tokens; // the list of generated tokens
  41. private $RDP_count; // total number of $RDP_tokens
  42. private $RDP_pos; // position within the $token array while processing equation
  43. private $RDP_errs; // array of syntax errors
  44. private $RDP_onlyparse;
  45. private $RDP_stack; // stack of intermediate results
  46. private $RDP_result; // final result of evaluating the expression;
  47. private $RDP_evalStatus; // true if $RDP_result is a valid result, and there are no serious errors
  48. private $varsUsed; // list of variables referenced in the equation
  49. // These variables are only used by sProcessStringContainingExpressions
  50. private $allVarsUsed; // full list of variables used within the string, even if contains multiple expressions
  51. private $prettyPrintSource; // HTML formatted output of running sProcessStringContainingExpressions
  52. private $substitutionNum; // Keeps track of number of substitions performed XXX
  53. private $substitutionInfo; // array of JavaScripts to managing dynamic substitution
  54. private $jsExpression; // caches computation of JavaScript equivalent for an Expression
  55. private $questionSeq; // sequence order of question - so can detect if try to use variable before it is set
  56. private $groupSeq; // sequence order of groups - so can detect if try to use variable before it is set
  57. private $surveyMode='group';
  58. // The following are only needed to enable click on variable names within pretty print and open new window to edit them
  59. private $sid=NULL; // the survey ID
  60. private $hyperlinkSyntaxHighlighting=true; // TODO - change this back to false
  61. private $sgqaNaming=false;
  62. function __construct()
  63. {
  64. // List of token-matching regular expressions
  65. // Note, this is effectively a Lexer using Regular Expressions. Don't change this unless you understand compiler design.
  66. $RDP_regex_dq_string = '(?<!\\\\)".*?(?<!\\\\)"';
  67. $RDP_regex_sq_string = '(?<!\\\\)\'.*?(?<!\\\\)\'';
  68. $RDP_regex_whitespace = '\s+';
  69. $RDP_regex_lparen = '\(';
  70. $RDP_regex_rparen = '\)';
  71. $RDP_regex_comma = ',';
  72. $RDP_regex_not = '!';
  73. $RDP_regex_inc_dec = '\+\+|--';
  74. $RDP_regex_binary = '[+*/-]';
  75. $RDP_regex_compare = '<=|<|>=|>|==|!=|\ble\b|\blt\b|\bge\b|\bgt\b|\beq\b|\bne\b';
  76. $RDP_regex_assign = '='; // '=|\+=|-=|\*=|/=';
  77. $RDP_regex_sgqa = '(?:INSERTANS:)?[0-9]+X[0-9]+X[0-9]+[A-Z0-9_]*\#?[01]?(?:\.(?:' . ExpressionManager::$RDP_regex_var_attr . '))?';
  78. $RDP_regex_word = '(?:TOKEN:)?(?:[A-Z][A-Z0-9_]*)?(?:\.(?:[A-Z][A-Z0-9_]*))*(?:\.(?:' . ExpressionManager::$RDP_regex_var_attr . '))?';
  79. $RDP_regex_number = '[0-9]+\.?[0-9]*|\.[0-9]+';
  80. $RDP_regex_andor = '\band\b|\bor\b|&&|\|\|';
  81. $RDP_regex_lcb = '{';
  82. $RDP_regex_rcb = '}';
  83. $RDP_regex_sq = '\'';
  84. $RDP_regex_dq= '"';
  85. $RDP_regex_bs = '\\\\';
  86. $RDP_StringSplitRegex = array(
  87. $RDP_regex_lcb,
  88. $RDP_regex_rcb,
  89. $RDP_regex_sq,
  90. $RDP_regex_dq,
  91. $RDP_regex_bs,
  92. );
  93. // RDP_ExpressionRegex is the regular expression that splits apart strings that contain curly braces in order to find expressions
  94. $this->RDP_ExpressionRegex = '#(' . implode('|',$RDP_StringSplitRegex) . ')#i';
  95. // asTokenRegex and RDP_TokenType must be kept in sync (same number and order)
  96. $RDP_TokenRegex = array(
  97. $RDP_regex_dq_string,
  98. $RDP_regex_sq_string,
  99. $RDP_regex_whitespace,
  100. $RDP_regex_lparen,
  101. $RDP_regex_rparen,
  102. $RDP_regex_comma,
  103. $RDP_regex_andor,
  104. $RDP_regex_compare,
  105. $RDP_regex_sgqa,
  106. $RDP_regex_word,
  107. $RDP_regex_number,
  108. $RDP_regex_not,
  109. $RDP_regex_inc_dec,
  110. $RDP_regex_assign,
  111. $RDP_regex_binary,
  112. );
  113. $this->RDP_TokenType = array(
  114. 'DQ_STRING',
  115. 'SQ_STRING',
  116. 'SPACE',
  117. 'LP',
  118. 'RP',
  119. 'COMMA',
  120. 'AND_OR',
  121. 'COMPARE',
  122. 'SGQA',
  123. 'WORD',
  124. 'NUMBER',
  125. 'NOT',
  126. 'OTHER',
  127. 'ASSIGN',
  128. 'BINARYOP',
  129. );
  130. // $RDP_TokenizerRegex - a single regex used to split and equation into tokens
  131. $this->RDP_TokenizerRegex = '#(' . implode('|',$RDP_TokenRegex) . ')#i';
  132. // $RDP_CategorizeTokensRegex - an array of patterns so can categorize the type of token found - would be nice if could get this from preg_split
  133. // Adding ability to capture 'OTHER' type, which indicates an error - unsupported syntax element
  134. $this->RDP_CategorizeTokensRegex = preg_replace("#^(.*)$#","#^$1$#i",$RDP_TokenRegex);
  135. $this->RDP_CategorizeTokensRegex[] = '/.+/';
  136. $this->RDP_TokenType[] = 'OTHER';
  137. // Each allowed function is a mapping from local name to external name + number of arguments
  138. // Functions can have a list of serveral allowable #s of arguments.
  139. // If the value is -1, the function must have a least one argument but can have an unlimited number of them
  140. // -2 means that at least one argument is required. -3 means at least two arguments are required, etc.
  141. $this->RDP_ValidFunctions = array(
  142. 'abs' => array('abs', 'Math.abs', $this->gT('Absolute value'), 'number abs(number)', 'http://www.php.net/manual/en/function.checkdate.php', 1),
  143. 'acos' => array('acos', 'Math.acos', $this->gT('Arc cosine'), 'number acos(number)', 'http://www.php.net/manual/en/function.acos.php', 1),
  144. 'addslashes' => array('addslashes', $this->gT('addslashes'), 'Quote string with slashes', 'string addslashes(string)', 'http://www.php.net/manual/en/function.addslashes.php', 1),
  145. 'asin' => array('asin', 'Math.asin', $this->gT('Arc sine'), 'number asin(number)', 'http://www.php.net/manual/en/function.asin.php', 1),
  146. 'atan' => array('atan', 'Math.atan', $this->gT('Arc tangent'), 'number atan(number)', 'http://www.php.net/manual/en/function.atan.php', 1),
  147. 'atan2' => array('atan2', 'Math.atan2', $this->gT('Arc tangent of two variables'), 'number atan2(number, number)', 'http://www.php.net/manual/en/function.atan2.php', 2),
  148. 'ceil' => array('ceil', 'Math.ceil', $this->gT('Round fractions up'), 'number ceil(number)', 'http://www.php.net/manual/en/function.ceil.php', 1),
  149. 'checkdate' => array('checkdate', 'checkdate', $this->gT('Returns true(1) if it is a valid date in gregorian calendar'), 'bool checkdate(month,day,year)', 'http://www.php.net/manual/en/function.checkdate.php', 3),
  150. 'cos' => array('cos', 'Math.cos', $this->gT('Cosine'), 'number cos(number)', 'http://www.php.net/manual/en/function.cos.php', 1),
  151. 'count' => array('exprmgr_count', 'LEMcount', $this->gT('Count the number of answered questions in the list'), 'number count(arg1, arg2, ... argN)', '', -1),
  152. 'countif' => array('exprmgr_countif', 'LEMcountif', $this->gT('Count the number of answered questions in the list equal the first argument'), 'number countif(matches, arg1, arg2, ... argN)', '', -2),
  153. 'countifop' => array('exprmgr_countifop', 'LEMcountifop', $this->gT('Count the number of answered questions in the list which pass the critiera (arg op value)'), 'number countifop(op, value, arg1, arg2, ... argN)', '', -3),
  154. 'date' => array('date', 'date', $this->gT('Format a local date/time'), 'string date(format [, timestamp=time()])', 'http://www.php.net/manual/en/function.date.php', 1,2),
  155. 'exp' => array('exp', 'Math.exp', $this->gT('Calculates the exponent of e'), 'number exp(number)', 'http://www.php.net/manual/en/function.exp.php', 1),
  156. 'fixnum' => array('exprmgr_fixnum', 'LEMfixnum', $this->gT('Display numbers with comma as decimal separator, if needed'), 'string fixnum(number)', '', 1),
  157. 'floor' => array('floor', 'Math.floor', $this->gT('Round fractions down'), 'number floor(number)', 'http://www.php.net/manual/en/function.floor.php', 1),
  158. 'gmdate' => array('gmdate', 'gmdate', $this->gT('Format a GMT date/time'), 'string gmdate(format [, timestamp=time()])', 'http://www.php.net/manual/en/function.gmdate.php', 1,2),
  159. 'html_entity_decode' => array('html_entity_decode', 'html_entity_decode', $this->gT('Convert all HTML entities to their applicable characters (always uses ENT_QUOTES and UTF-8)'), 'string html_entity_decode(string)', 'http://www.php.net/manual/en/function.html-entity-decode.php', 1),
  160. 'htmlentities' => array('htmlentities', 'htmlentities', $this->gT('Convert all applicable characters to HTML entities (always uses ENT_QUOTES and UTF-8)'), 'string htmlentities(string)', 'http://www.php.net/manual/en/function.htmlentities.php', 1),
  161. 'htmlspecialchars' => array('expr_mgr_htmlspecialchars', 'htmlspecialchars', $this->gT('Convert special characters to HTML entities (always uses ENT_QUOTES and UTF-8)'), 'string htmlspecialchars(string)', 'http://www.php.net/manual/en/function.htmlspecialchars.php', 1),
  162. 'htmlspecialchars_decode' => array('expr_mgr_htmlspecialchars_decode', 'htmlspecialchars_decode', $this->gT('Convert special HTML entities back to characters (always uses ENT_QUOTES and UTF-8)'), 'string htmlspecialchars_decode(string)', 'http://www.php.net/manual/en/function.htmlspecialchars-decode.php', 1),
  163. 'idate' => array('idate', 'idate', $this->gT('Format a local time/date as integer'), 'string idate(string [, timestamp=time()])', 'http://www.php.net/manual/en/function.idate.php', 1,2),
  164. 'if' => array('exprmgr_if', 'LEMif', $this->gT('Conditional processing'), 'if(test,result_if_true,result_if_false)', '', 3),
  165. 'implode' => array('exprmgr_implode', 'LEMimplode', $this->gT('Join array elements with a string'), 'string implode(glue,arg1,arg2,...,argN)', 'http://www.php.net/manual/en/function.implode.php', -2),
  166. 'intval' => array('intval', 'LEMintval', $this->gT('Get the integer value of a variable'), 'int intval(number [, base=10])', 'http://www.php.net/manual/en/function.intval.php', 1,2),
  167. 'is_empty' => array('exprmgr_empty', 'LEMempty', $this->gT('Determine whether a variable is considered to be empty'), 'bool is_empty(var)', 'http://www.php.net/manual/en/function.empty.php', 1),
  168. 'is_float' => array('is_float', 'LEMis_float', $this->gT('Finds whether the type of a variable is float'), 'bool is_float(var)', 'http://www.php.net/manual/en/function.is-float.php', 1),
  169. 'is_int' => array('is_int', 'LEMis_int', $this->gT('Find whether the type of a variable is integer'), 'bool is_int(var)', 'http://www.php.net/manual/en/function.is-int.php', 1),
  170. 'is_nan' => array('is_nan', 'isNaN', $this->gT('Finds whether a value is not a number'), 'bool is_nan(var)', 'http://www.php.net/manual/en/function.is-nan.php', 1),
  171. 'is_null' => array('is_null', 'LEMis_null', $this->gT('Finds whether a variable is NULL'), 'bool is_null(var)', 'http://www.php.net/manual/en/function.is-null.php', 1),
  172. 'is_numeric' => array('is_numeric', 'LEMis_numeric', $this->gT('Finds whether a variable is a number or a numeric string'), 'bool is_numeric(var)', 'http://www.php.net/manual/en/function.is-numeric.php', 1),
  173. 'is_string' => array('is_string', 'LEMis_string', $this->gT('Find whether the type of a variable is string'), 'bool is_string(var)', 'http://www.php.net/manual/en/function.is-string.php', 1),
  174. 'join' => array('exprmgr_join', 'LEMjoin', $this->gT('Join strings, return joined string.This function is an alias of implode("",argN)'), 'string join(arg1,arg2,...,argN)', '', -1),
  175. 'list' => array('exprmgr_list', 'LEMlist', $this->gT('Return comma-separated list of values'), 'string list(arg1, arg2, ... argN)', '', -2),
  176. 'log' => array('log', 'Math.log', $this->gT('Natural logarithm'), 'number log(number)', 'http://www.php.net/manual/en/function.log.php', 1),
  177. 'ltrim' => array('ltrim', 'ltrim', $this->gT('Strip whitespace (or other characters) from the beginning of a string'), 'string ltrim(string [, charlist])', 'http://www.php.net/manual/en/function.ltrim.php', 1,2),
  178. 'max' => array('max', 'Math.max', $this->gT('Find highest value'), 'number max(arg1, arg2, ... argN)', 'http://www.php.net/manual/en/function.max.php', -2),
  179. 'min' => array('min', 'Math.min', $this->gT('Find lowest value'), 'number min(arg1, arg2, ... argN)', 'http://www.php.net/manual/en/function.min.php', -2),
  180. 'mktime' => array('mktime', 'mktime', $this->gT('Get UNIX timestamp for a date (each of the 6 arguments are optional)'), 'number mktime([hour [, minute [, second [, month [, day [, year ]]]]]])', 'http://www.php.net/manual/en/function.mktime.php', 0,1,2,3,4,5,6),
  181. 'nl2br' => array('nl2br', 'nl2br', $this->gT('Inserts HTML line breaks before all newlines in a string'), 'string nl2br(string)', 'http://www.php.net/manual/en/function.nl2br.php', 1,1),
  182. 'number_format' => array('number_format', 'number_format', $this->gT('Format a number with grouped thousands'), 'string number_format(number)', 'http://www.php.net/manual/en/function.number-format.php', 1),
  183. 'pi' => array('pi', 'LEMpi', $this->gT('Get value of pi'), 'number pi()', '', 0),
  184. 'pow' => array('pow', 'Math.pow', $this->gT('Exponential expression'), 'number pow(base, exp)', 'http://www.php.net/manual/en/function.pow.php', 2),
  185. 'quoted_printable_decode' => array('quoted_printable_decode', 'quoted_printable_decode', $this->gT('Convert a quoted-printable string to an 8 bit string'), 'string quoted_printable_decode(string)', 'http://www.php.net/manual/en/function.quoted-printable-decode.php', 1),
  186. 'quoted_printable_encode' => array('quoted_printable_encode', 'quoted_printable_encode', $this->gT('Convert a 8 bit string to a quoted-printable string'), 'string quoted_printable_encode(string)', 'http://www.php.net/manual/en/function.quoted-printable-encode.php', 1),
  187. 'quotemeta' => array('quotemeta', 'quotemeta', $this->gT('Quote meta characters'), 'string quotemeta(string)', 'http://www.php.net/manual/en/function.quotemeta.php', 1),
  188. 'rand' => array('rand', 'rand', $this->gT('Generate a random integer'), 'int rand() OR int rand(min, max)', 'http://www.php.net/manual/en/function.rand.php', 0,2),
  189. 'regexMatch' => array('exprmgr_regexMatch', 'LEMregexMatch', $this->gT('Compare a string to a regular expression pattern'), 'bool regexMatch(pattern,input)', '', 2),
  190. 'round' => array('round', 'round', $this->gT('Rounds a number to an optional precision'), 'number round(val [, precision])', 'http://www.php.net/manual/en/function.round.php', 1,2),
  191. 'rtrim' => array('rtrim', 'rtrim', $this->gT('Strip whitespace (or other characters) from the end of a string'), 'string rtrim(string [, charlist])', 'http://www.php.net/manual/en/function.rtrim.php', 1,2),
  192. 'sin' => array('sin', 'Math.sin', $this->gT('Sine'), 'number sin(arg)', 'http://www.php.net/manual/en/function.sin.php', 1),
  193. 'sprintf' => array('sprintf', 'sprintf', $this->gT('Return a formatted string'), 'string sprintf(format, arg1, arg2, ... argN)', 'http://www.php.net/manual/en/function.sprintf.php', -2),
  194. 'sqrt' => array('sqrt', 'Math.sqrt', $this->gT('Square root'), 'number sqrt(arg)', 'http://www.php.net/manual/en/function.sqrt.php', 1),
  195. 'stddev' => array('exprmgr_stddev', 'LEMstddev', $this->gT('Calculate the Sample Standard Deviation for the list of numbers'), 'number stddev(arg1, arg2, ... argN)', '', -2),
  196. 'str_pad' => array('str_pad', 'str_pad', $this->gT('Pad a string to a certain length with another string'), 'string str_pad(input, pad_length [, pad_string])', 'http://www.php.net/manual/en/function.str-pad.php', 2,3),
  197. 'str_repeat' => array('str_repeat', 'str_repeat', $this->gT('Repeat a string'), 'string str_repeat(input, multiplier)', 'http://www.php.net/manual/en/function.str-repeat.php', 2),
  198. 'str_replace' => array('str_replace', 'LEMstr_replace', $this->gT('Replace all occurrences of the search string with the replacement string'), 'string str_replace(search, replace, subject)', 'http://www.php.net/manual/en/function.str-replace.php', 3),
  199. 'strcasecmp' => array('strcasecmp', 'strcasecmp', $this->gT('Binary safe case-insensitive string comparison'), 'int strcasecmp(str1, str2)', 'http://www.php.net/manual/en/function.strcasecmp.php', 2),
  200. 'strcmp' => array('strcmp', 'strcmp', $this->gT('Binary safe string comparison'), 'int strcmp(str1, str2)', 'http://www.php.net/manual/en/function.strcmp.php', 2),
  201. 'strip_tags' => array('strip_tags', 'strip_tags', $this->gT('Strip HTML and PHP tags from a string'), 'string strip_tags(str, allowable_tags)', 'http://www.php.net/manual/en/function.strip-tags.php', 1,2),
  202. 'stripos' => array('stripos', 'stripos', $this->gT('Find position of first occurrence of a case-insensitive string'), 'int stripos(haystack, needle [, offset=0])', 'http://www.php.net/manual/en/function.stripos.php', 2,3),
  203. 'stripslashes' => array('stripslashes', 'stripslashes', $this->gT('Un-quotes a quoted string'), 'string stripslashes(string)', 'http://www.php.net/manual/en/function.stripslashes.php', 1),
  204. 'stristr' => array('stristr', 'stristr', $this->gT('Case-insensitive strstr'), 'string stristr(haystack, needle [, before_needle=false])', 'http://www.php.net/manual/en/function.stristr.php', 2,3),
  205. 'strlen' => array('strlen', 'LEMstrlen', $this->gT('Get string length'), 'int strlen(string)', 'http://www.php.net/manual/en/function.strlen.php', 1),
  206. 'strpos' => array('strpos', 'LEMstrpos', $this->gT('Find position of first occurrence of a string'), 'int strpos(haystack, needle [ offset=0])', 'http://www.php.net/manual/en/function.strpos.php', 2,3),
  207. 'strrev' => array('strrev', 'strrev', $this->gT('Reverse a string'), 'string strrev(string)', 'http://www.php.net/manual/en/function.strrev.php', 1),
  208. 'strstr' => array('strstr', 'strstr', $this->gT('Find first occurrence of a string'), 'string strstr(haystack, needle)', 'http://www.php.net/manual/en/function.strstr.php', 2),
  209. 'strtolower' => array('strtolower', 'LEMstrtolower', $this->gT('Make a string lowercase'), 'string strtolower(string)', 'http://www.php.net/manual/en/function.strtolower.php', 1),
  210. 'strtoupper' => array('strtoupper', 'LEMstrtoupper', $this->gT('Make a string uppercase'), 'string strtoupper(string)', 'http://www.php.net/manual/en/function.strtoupper.php', 1),
  211. 'substr' => array('substr', 'substr', $this->gT('Return part of a string'), 'string substr(string, start [, length])', 'http://www.php.net/manual/en/function.substr.php', 2,3),
  212. 'sum' => array('array_sum', 'LEMsum', $this->gT('Calculate the sum of values in an array'), 'number sum(arg1, arg2, ... argN)', '', -2),
  213. 'sumifop' => array('exprmgr_sumifop', 'LEMsumifop', $this->gT('Sum the values of answered questions in the list which pass the critiera (arg op value)'), 'number sumifop(op, value, arg1, arg2, ... argN)', '', -3),
  214. 'tan' => array('tan', 'Math.tan', $this->gT('Tangent'), 'number tan(arg)', 'http://www.php.net/manual/en/function.tan.php', 1),
  215. 'time' => array('time', 'time', $this->gT('Return current UNIX timestamp'), 'number time()', 'http://www.php.net/manual/en/function.time.php', 0),
  216. 'trim' => array('trim', 'trim', $this->gT('Strip whitespace (or other characters) from the beginning and end of a string'), 'string trim(string [, charlist])', 'http://www.php.net/manual/en/function.trim.php', 1,2),
  217. 'ucwords' => array('ucwords', 'ucwords', $this->gT('Uppercase the first character of each word in a string'), 'string ucwords(string)', 'http://www.php.net/manual/en/function.ucwords.php', 1),
  218. 'unique' => array('exprmgr_unique', 'LEMunique', $this->gT('Returns true if all non-empty responses are unique'), 'boolean unique(arg1, ..., argN)', '', -1),
  219. );
  220. }
  221. /**
  222. * Add an error to the error log
  223. *
  224. * @param <type> $errMsg
  225. * @param <type> $token
  226. */
  227. private function RDP_AddError($errMsg, $token)
  228. {
  229. $this->RDP_errs[] = array($errMsg, $token);
  230. }
  231. /**
  232. * RDP_EvaluateBinary() computes binary expressions, such as (a or b), (c * d), popping the top two entries off the
  233. * stack and pushing the result back onto the stack.
  234. *
  235. * @param array $token
  236. * @return boolean - false if there is any error, else true
  237. */
  238. private function RDP_EvaluateBinary(array $token)
  239. {
  240. if (count($this->RDP_stack) < 2)
  241. {
  242. $this->RDP_AddError($this->gT("Unable to evaluate binary operator - fewer than 2 entries on stack"), $token);
  243. return false;
  244. }
  245. $arg2 = $this->RDP_StackPop();
  246. $arg1 = $this->RDP_StackPop();
  247. if (is_null($arg1) or is_null($arg2))
  248. {
  249. $this->RDP_AddError($this->gT("Invalid value(s) on the stack"), $token);
  250. return false;
  251. }
  252. // TODO: try to determine datatype?
  253. $bNumericArg1 = is_numeric($arg1[0]) || $arg1[0] == '';
  254. $bNumericArg2 = is_numeric($arg2[0]) || $arg2[0] == '';
  255. $bStringArg1 = !$bNumericArg1 || $arg1[0] == '';
  256. $bStringArg2 = !$bNumericArg2 || $arg2[0] == '';
  257. $bBothNumeric = ($bNumericArg1 && $bNumericArg2);
  258. $bBothString = ($bStringArg1 && $bStringArg2);
  259. $bMismatchType = (!$bBothNumeric && !$bBothString);
  260. switch(strtolower($token[0]))
  261. {
  262. case 'or':
  263. case '||':
  264. $result = array(($arg1[0] or $arg2[0]),$token[1],'NUMBER');
  265. break;
  266. case 'and':
  267. case '&&':
  268. $result = array(($arg1[0] and $arg2[0]),$token[1],'NUMBER');
  269. break;
  270. case '==':
  271. case 'eq':
  272. $result = array(($arg1[0] == $arg2[0]),$token[1],'NUMBER');
  273. break;
  274. case '!=':
  275. case 'ne':
  276. $result = array(($arg1[0] != $arg2[0]),$token[1],'NUMBER');
  277. break;
  278. case '<':
  279. case 'lt':
  280. if ($bMismatchType) {
  281. $result = array(false,$token[1],'NUMBER');
  282. }
  283. else {
  284. $result = array(($arg1[0] < $arg2[0]),$token[1],'NUMBER');
  285. }
  286. break;
  287. case '<=';
  288. case 'le':
  289. if ($bMismatchType) {
  290. $result = array(false,$token[1],'NUMBER');
  291. }
  292. else {
  293. // Need this explicit comparison in order to be in agreement with JavaScript
  294. if (($arg1[0] == '0' && $arg2[0] == '') || ($arg1[0] == '' && $arg2[0] == '0')) {
  295. $result = array(true,$token[1],'NUMBER');
  296. }
  297. else {
  298. $result = array(($arg1[0] <= $arg2[0]),$token[1],'NUMBER');
  299. }
  300. }
  301. break;
  302. case '>':
  303. case 'gt':
  304. if ($bMismatchType) {
  305. $result = array(false,$token[1],'NUMBER');
  306. }
  307. else {
  308. // Need this explicit comparison in order to be in agreement with JavaScript
  309. if (($arg1[0] == '0' && $arg2[0] == '') || ($arg1[0] == '' && $arg2[0] == '0')) {
  310. $result = array(false,$token[1],'NUMBER');
  311. }
  312. else {
  313. $result = array(($arg1[0] > $arg2[0]),$token[1],'NUMBER');
  314. }
  315. }
  316. break;
  317. case '>=';
  318. case 'ge':
  319. if ($bMismatchType) {
  320. $result = array(false,$token[1],'NUMBER');
  321. }
  322. else {
  323. $result = array(($arg1[0] >= $arg2[0]),$token[1],'NUMBER');
  324. }
  325. break;
  326. case '+':
  327. if ($bBothNumeric) {
  328. $result = array(($arg1[0] + $arg2[0]),$token[1],'NUMBER');
  329. }
  330. else {
  331. $result = array($arg1[0] . $arg2[0],$token[1],'STRING');
  332. }
  333. break;
  334. case '-':
  335. if ($bBothNumeric) {
  336. $result = array(($arg1[0] - $arg2[0]),$token[1],'NUMBER');
  337. }
  338. else {
  339. $result = array(NAN,$token[1],'NUMBER');
  340. }
  341. break;
  342. case '*':
  343. if ($bBothNumeric) {
  344. $result = array(($arg1[0] * $arg2[0]),$token[1],'NUMBER');
  345. }
  346. else {
  347. $result = array(NAN,$token[1],'NUMBER');
  348. }
  349. break;
  350. case '/';
  351. if ($bBothNumeric) {
  352. if ($arg2[0] == 0) {
  353. $result = array(NAN,$token[1],'NUMBER');
  354. }
  355. else {
  356. $result = array(($arg1[0] / $arg2[0]),$token[1],'NUMBER');
  357. }
  358. }
  359. else {
  360. $result = array(NAN,$token[1],'NUMBER');
  361. }
  362. break;
  363. }
  364. $this->RDP_StackPush($result);
  365. return true;
  366. }
  367. /**
  368. * Processes operations like +a, -b, !c
  369. * @param array $token
  370. * @return boolean - true if success, false if any error occurred
  371. */
  372. private function RDP_EvaluateUnary(array $token)
  373. {
  374. if (count($this->RDP_stack) < 1)
  375. {
  376. $this->RDP_AddError($this->gT("Unable to evaluate unary operator - no entries on stack"), $token);
  377. return false;
  378. }
  379. $arg1 = $this->RDP_StackPop();
  380. if (is_null($arg1))
  381. {
  382. $this->RDP_AddError($this->gT("Invalid value(s) on the stack"), $token);
  383. return false;
  384. }
  385. // TODO: try to determine datatype?
  386. switch($token[0])
  387. {
  388. case '+':
  389. $result = array((+$arg1[0]),$token[1],'NUMBER');
  390. break;
  391. case '-':
  392. $result = array((-$arg1[0]),$token[1],'NUMBER');
  393. break;
  394. case '!';
  395. $result = array((!$arg1[0]),$token[1],'NUMBER');
  396. break;
  397. }
  398. $this->RDP_StackPush($result);
  399. return true;
  400. }
  401. /**
  402. * Main entry function
  403. * @param <type> $expr
  404. * @param <type> $onlyparse - if true, then validate the syntax without computing an answer
  405. * @return boolean - true if success, false if any error occurred
  406. */
  407. public function RDP_Evaluate($expr, $onlyparse=false)
  408. {
  409. $this->RDP_expr = $expr;
  410. $this->RDP_tokens = $this->RDP_Tokenize($expr);
  411. $this->RDP_count = count($this->RDP_tokens);
  412. $this->RDP_pos = -1; // starting position within array (first act will be to increment it)
  413. $this->RDP_errs = array();
  414. $this->RDP_onlyparse = $onlyparse;
  415. $this->RDP_stack = array();
  416. $this->RDP_evalStatus = false;
  417. $this->RDP_result = NULL;
  418. $this->varsUsed = array();
  419. $this->jsExpression = NULL;
  420. if ($this->HasSyntaxErrors()) {
  421. return false;
  422. }
  423. elseif ($this->RDP_EvaluateExpressions())
  424. {
  425. if ($this->RDP_pos < $this->RDP_count)
  426. {
  427. $this->RDP_AddError($this->gT("Extra tokens found"), $this->RDP_tokens[$this->RDP_pos]);
  428. return false;
  429. }
  430. $this->RDP_result = $this->RDP_StackPop();
  431. if (is_null($this->RDP_result))
  432. {
  433. return false;
  434. }
  435. if (count($this->RDP_stack) == 0)
  436. {
  437. $this->RDP_evalStatus = true;
  438. return true;
  439. }
  440. else
  441. {
  442. $this-RDP_AddError($this->gT("Unbalanced equation - values left on stack"),NULL);
  443. return false;
  444. }
  445. }
  446. else
  447. {
  448. $this->RDP_AddError($this->gT("Not a valid expression"),NULL);
  449. return false;
  450. }
  451. }
  452. /**
  453. * Process "a op b" where op in (+,-,concatenate)
  454. * @return boolean - true if success, false if any error occurred
  455. */
  456. private function RDP_EvaluateAdditiveExpression()
  457. {
  458. if (!$this->RDP_EvaluateMultiplicativeExpression())
  459. {
  460. return false;
  461. }
  462. while (($this->RDP_pos + 1) < $this->RDP_count)
  463. {
  464. $token = $this->RDP_tokens[++$this->RDP_pos];
  465. if ($token[2] == 'BINARYOP')
  466. {
  467. switch ($token[0])
  468. {
  469. case '+':
  470. case '-';
  471. if ($this->RDP_EvaluateMultiplicativeExpression())
  472. {
  473. if (!$this->RDP_EvaluateBinary($token))
  474. {
  475. return false;
  476. }
  477. // else continue;
  478. }
  479. else
  480. {
  481. return false;
  482. }
  483. break;
  484. default:
  485. --$this->RDP_pos;
  486. return true;
  487. }
  488. }
  489. else
  490. {
  491. --$this->RDP_pos;
  492. return true;
  493. }
  494. }
  495. return true;
  496. }
  497. /**
  498. * Process a Constant (number of string), retrieve the value of a known variable, or process a function, returning result on the stack.
  499. * @return boolean - true if success, false if any error occurred
  500. */
  501. private function RDP_EvaluateConstantVarOrFunction()
  502. {
  503. if ($this->RDP_pos + 1 >= $this->RDP_count)
  504. {
  505. $this->RDP_AddError($this->gT("Poorly terminated expression - expected a constant or variable"), NULL);
  506. return false;
  507. }
  508. $token = $this->RDP_tokens[++$this->RDP_pos];
  509. switch ($token[2])
  510. {
  511. case 'NUMBER':
  512. case 'DQ_STRING':
  513. case 'SQ_STRING':
  514. $this->RDP_StackPush($token);
  515. return true;
  516. break;
  517. case 'WORD':
  518. case 'SGQA':
  519. if (($this->RDP_pos + 1) < $this->RDP_count and $this->RDP_tokens[($this->RDP_pos + 1)][2] == 'LP')
  520. {
  521. return $this->RDP_EvaluateFunction();
  522. }
  523. else
  524. {
  525. if ($this->RDP_isValidVariable($token[0]))
  526. {
  527. $this->varsUsed[] = $token[0]; // add this variable to list of those used in this equation
  528. if (preg_match("/\.(gid|grelevance|gseq|jsName|mandatory|qid|qseq|question|readWrite|relevance|rowdivid|sgqa|type)$/",$token[0]))
  529. {
  530. $relStatus=1; // static, so always relevant
  531. }
  532. else
  533. {
  534. $relStatus = $this->GetVarAttribute($token[0],'relevanceStatus',1);
  535. }
  536. if ($relStatus==1)
  537. {
  538. $result = array($this->GetVarAttribute($token[0],NULL,''),$token[1],'NUMBER');
  539. }
  540. else
  541. {
  542. $result = array(NULL,$token[1],'NUMBER'); // was 0 instead of NULL
  543. }
  544. $this->RDP_StackPush($result);
  545. // TODO - currently, will try to process value anyway, but want to show a potential error. Should it be a definitive error (e.g. prevent this behavior)?
  546. $groupSeq = $this->GetVarAttribute($token[0],'gseq',-1);
  547. if (($groupSeq != -1 && $this->groupSeq != -1) && ($groupSeq > $this->groupSeq))
  548. {
  549. $this->RDP_AddError($this->gT("Variable not declared until a later page"),$token);
  550. return false;
  551. }
  552. return true;
  553. }
  554. else
  555. {
  556. $this->RDP_AddError($this->gT("Undefined variable"), $token);
  557. return false;
  558. }
  559. }
  560. break;
  561. case 'COMMA':
  562. --$this->RDP_pos;
  563. $this->RDP_AddError($this->gT("Should never get to this line?"),$token);
  564. return false;
  565. default:
  566. return false;
  567. break;
  568. }
  569. }
  570. /**
  571. * Process "a == b", "a eq b", "a != b", "a ne b"
  572. * @return boolean - true if success, false if any error occurred
  573. */
  574. private function RDP_EvaluateEqualityExpression()
  575. {
  576. if (!$this->RDP_EvaluateRelationExpression())
  577. {
  578. return false;
  579. }
  580. while (($this->RDP_pos + 1) < $this->RDP_count)
  581. {
  582. $token = $this->RDP_tokens[++$this->RDP_pos];
  583. switch (strtolower($token[0]))
  584. {
  585. case '==':
  586. case 'eq':
  587. case '!=':
  588. case 'ne':
  589. if ($this->RDP_EvaluateRelationExpression())
  590. {
  591. if (!$this->RDP_EvaluateBinary($token))
  592. {
  593. return false;
  594. }
  595. // else continue;
  596. }
  597. else
  598. {
  599. return false;
  600. }
  601. break;
  602. default:
  603. --$this->RDP_pos;
  604. return true;
  605. }
  606. }
  607. return true;
  608. }
  609. /**
  610. * Process a single expression (e.g. without commas)
  611. * @return boolean - true if success, false if any error occurred
  612. */
  613. private function RDP_EvaluateExpression()
  614. {
  615. if ($this->RDP_pos + 2 < $this->RDP_count)
  616. {
  617. $token1 = $this->RDP_tokens[++$this->RDP_pos];
  618. $token2 = $this->RDP_tokens[++$this->RDP_pos];
  619. if ($token2[2] == 'ASSIGN')
  620. {
  621. if ($this->RDP_isValidVariable($token1[0]))
  622. {
  623. $this->varsUsed[] = $token1[0]; // add this variable to list of those used in this equation
  624. if ($this->RDP_isWritableVariable($token1[0]))
  625. {
  626. $evalStatus = $this->RDP_EvaluateLogicalOrExpression();
  627. if ($evalStatus)
  628. {
  629. $result = $this->RDP_StackPop();
  630. if (!is_null($result))
  631. {
  632. $newResult = $token2;
  633. $newResult[2] = 'NUMBER';
  634. $newResult[0] = $this->RDP_SetVariableValue($token2[0], $token1[0], $result[0]);
  635. $this->RDP_StackPush($newResult);
  636. }
  637. else
  638. {
  639. $evalStatus = false;
  640. }
  641. }
  642. return $evalStatus;
  643. }
  644. else
  645. {
  646. $this->RDP_AddError($this->gT('The value of this variable can not be changed'), $token1);
  647. return false;
  648. }
  649. }
  650. else
  651. {
  652. $this->RDP_AddError($this->gT('Only variables can be assigned values'), $token1);
  653. return false;
  654. }
  655. }
  656. else
  657. {
  658. // not an assignment expression, so try something else
  659. $this->RDP_pos -= 2;
  660. return $this->RDP_EvaluateLogicalOrExpression();
  661. }
  662. }
  663. else
  664. {
  665. return $this->RDP_EvaluateLogicalOrExpression();
  666. }
  667. }
  668. /**
  669. * Process "expression [, expression]*
  670. * @return boolean - true if success, false if any error occurred
  671. */
  672. private function RDP_EvaluateExpressions()
  673. {
  674. $evalStatus = $this->RDP_EvaluateExpression();
  675. if (!$evalStatus)
  676. {
  677. return false;
  678. }
  679. while (++$this->RDP_pos < $this->RDP_count) {
  680. $token = $this->RDP_tokens[$this->RDP_pos];
  681. if ($token[2] == 'RP')
  682. {
  683. return true; // presumbably the end of an expression
  684. }
  685. elseif ($token[2] == 'COMMA')
  686. {
  687. if ($this->RDP_EvaluateExpression())
  688. {
  689. $secondResult = $this->RDP_StackPop();
  690. $firstResult = $this->RDP_StackPop();
  691. if (is_null($firstResult))
  692. {
  693. return false;
  694. }
  695. $this->RDP_StackPush($secondResult);
  696. $evalStatus = true;
  697. }
  698. else
  699. {
  700. return false; // an error must have occurred
  701. }
  702. }
  703. else
  704. {
  705. $this->RDP_AddError($this->gT("Expected expressions separated by commas"),$token);
  706. $evalStatus = false;
  707. break;
  708. }
  709. }
  710. while (++$this->RDP_pos < $this->RDP_count)
  711. {
  712. $token = $this->RDP_tokens[$this->RDP_pos];
  713. $this->RDP_AddError($this->gT("Extra token found after expressions"),$token);
  714. $evalStatus = false;
  715. }
  716. return $evalStatus;
  717. }
  718. /**
  719. * Process a function call
  720. * @return boolean - true if success, false if any error occurred
  721. */
  722. private function RDP_EvaluateFunction()
  723. {
  724. $funcNameToken = $this->RDP_tokens[$this->RDP_pos]; // note that don't need to increment position for functions
  725. $funcName = $funcNameToken[0];
  726. if (!$this->RDP_isValidFunction($funcName))
  727. {
  728. $this->RDP_AddError($this->gT("Undefined function"), $funcNameToken);
  729. return false;
  730. }
  731. $token2 = $this->RDP_tokens[++$this->RDP_pos];
  732. if ($token2[2] != 'LP')
  733. {
  734. $this->RDP_AddError($this->gT("Expected left parentheses after function name"), $funcNameToken);
  735. }
  736. $params = array(); // will just store array of values, not tokens
  737. while ($this->RDP_pos + 1 < $this->RDP_count)
  738. {
  739. $token3 = $this->RDP_tokens[$this->RDP_pos + 1];
  740. if (count($params) > 0)
  741. {
  742. // should have COMMA or RP
  743. if ($token3[2] == 'COMMA')
  744. {
  745. ++$this->RDP_pos; // consume the token so can process next clause
  746. if ($this->RDP_EvaluateExpression())
  747. {
  748. $value = $this->RDP_StackPop();
  749. if (is_null($value))
  750. {
  751. return false;
  752. }
  753. $params[] = $value[0];
  754. continue;
  755. }
  756. else
  757. {
  758. $this->RDP_AddError($this->gT("Extra comma found in function"), $token3);
  759. return false;
  760. }
  761. }
  762. }
  763. if ($token3[2] == 'RP')
  764. {
  765. ++$this->RDP_pos; // consume the token so can process next clause
  766. return $this->RDP_RunFunction($funcNameToken,$params);
  767. }
  768. else
  769. {
  770. if ($this->RDP_EvaluateExpression())
  771. {
  772. $value = $this->RDP_StackPop();
  773. if (is_null($value))
  774. {
  775. return false;
  776. }
  777. $params[] = $value[0];
  778. continue;
  779. }
  780. else
  781. {
  782. return false;
  783. }
  784. }
  785. }
  786. }
  787. /**
  788. * Process "a && b" or "a and b"
  789. * @return boolean - true if success, false if any error occurred
  790. */
  791. private function RDP_EvaluateLogicalAndExpression()
  792. {
  793. if (!$this->RDP_EvaluateEqualityExpression())
  794. {
  795. return false;
  796. }
  797. while (($this->RDP_pos + 1) < $this->RDP_count)
  798. {
  799. $token = $this->RDP_tokens[++$this->RDP_pos];
  800. switch (strtolower($token[0]))
  801. {
  802. case '&&':
  803. case 'and':
  804. if ($this->RDP_EvaluateEqualityExpression())
  805. {
  806. if (!$this->RDP_EvaluateBinary($token))
  807. {
  808. return false;
  809. }
  810. // else continue
  811. }
  812. else
  813. {
  814. return false; // an error must have occurred
  815. }
  816. break;
  817. default:
  818. --$this->RDP_pos;
  819. return true;
  820. }
  821. }
  822. return true;
  823. }
  824. /**
  825. * Process "a || b" or "a or b"
  826. * @return boolean - true if success, false if any error occurred
  827. */
  828. private function RDP_EvaluateLogicalOrExpression()
  829. {
  830. if (!$this->RDP_EvaluateLogicalAndExpression())
  831. {
  832. return false;
  833. }
  834. while (($this->RDP_pos + 1) < $this->RDP_count)
  835. {
  836. $token = $this->RDP_tokens[++$this->RDP_pos];
  837. switch (strtolower($token[0]))
  838. {
  839. case '||':
  840. case 'or':
  841. if ($this->RDP_EvaluateLogicalAndExpression())
  842. {
  843. if (!$this->RDP_EvaluateBinary($token))
  844. {
  845. return false;
  846. }
  847. // else continue
  848. }
  849. else
  850. {
  851. // an error must have occurred
  852. return false;
  853. }
  854. break;
  855. default:
  856. // no more expressions being ORed together, so continue parsing
  857. --$this->RDP_pos;
  858. return true;
  859. }
  860. }
  861. // no more tokens to parse
  862. return true;
  863. }
  864. /**
  865. * Process "a op b" where op in (*,/)
  866. * @return boolean - true if success, false if any error occurred
  867. */
  868. private function RDP_EvaluateMultiplicativeExpression()
  869. {
  870. if (!$this->RDP_EvaluateUnaryExpression())
  871. {
  872. return false;
  873. }
  874. while (($this->RDP_pos + 1) < $this->RDP_count)
  875. {
  876. $token = $this->RDP_tokens[++$this->RDP_pos];
  877. if ($token[2] == 'BINARYOP')
  878. {
  879. switch ($token[0])
  880. {
  881. case '*':
  882. case '/';
  883. if ($this->RDP_EvaluateUnaryExpression())
  884. {
  885. if (!$this->RDP_EvaluateBinary($token))
  886. {
  887. return false;
  888. }
  889. // else continue
  890. }
  891. else
  892. {
  893. // an error must have occurred
  894. return false;
  895. }
  896. break;
  897. break;
  898. default:
  899. --$this->RDP_pos;
  900. return true;
  901. }
  902. }
  903. else
  904. {
  905. --$this->RDP_pos;
  906. return true;
  907. }
  908. }
  909. return true;
  910. }
  911. /**
  912. * Process expressions including functions and parenthesized blocks
  913. * @return boolean - true if success, false if any error occurred
  914. */
  915. private function RDP_EvaluatePrimaryExpression()
  916. {
  917. if (($this->RDP_pos + 1) >= $this->RDP_count) {
  918. $this->RDP_AddError($this->gT("Poorly terminated expression - expected a constant or variable"), NULL);
  919. return false;
  920. }
  921. $token = $this->RDP_tokens[++$this->RDP_pos];
  922. if ($token[2] == 'LP')
  923. {
  924. if (!$this->RDP_EvaluateExpressions())
  925. {
  926. return false;
  927. }
  928. $token = $this->RDP_tokens[$this->RDP_pos];
  929. if ($token[2] == 'RP')
  930. {
  931. return true;
  932. }
  933. else
  934. {
  935. $this->RDP_AddError($this->gT("Expected right parentheses"), $token);
  936. return false;
  937. }
  938. }
  939. else
  940. {
  941. --$this->RDP_pos;
  942. return $this->RDP_EvaluateConstantVarOrFunction();
  943. }
  944. }
  945. /**
  946. * Process "a op b" where op in (lt, gt, le, ge, <, >, <=, >=)
  947. * @return boolean - true if success, false if any error occurred
  948. */
  949. private function RDP_EvaluateRelationExpression()
  950. {
  951. if (!$this->RDP_EvaluateAdditiveExpression())
  952. {
  953. return false;
  954. }
  955. while (($this->RDP_pos + 1) < $this->RDP_count)
  956. {
  957. $token = $this->RDP_tokens[++$this->RDP_pos];
  958. switch (strtolower($token[0]))
  959. {
  960. case '<':
  961. case 'lt':
  962. case '<=';
  963. case 'le':
  964. case '>':
  965. case 'gt':
  966. case '>=';
  967. case 'ge':
  968. if ($this->RDP_EvaluateAdditiveExpression())
  969. {
  970. if (!$this->RDP_EvaluateBinary($token))
  971. {
  972. return false;
  973. }
  974. // else continue
  975. }
  976. else
  977. {
  978. // an error must have occurred
  979. return false;
  980. }
  981. break;
  982. default:
  983. --$this->RDP_pos;
  984. return true;
  985. }
  986. }
  987. return true;
  988. }
  989. /**
  990. * Process "op a" where op in (+,-,!)
  991. * @return boolean - true if success, false if any error occurred
  992. */
  993. private function RDP_EvaluateUnaryExpression()
  994. {
  995. if (($this->RDP_pos + 1) >= $this->RDP_count) {
  996. $this->RDP_AddError($this->gT("Poorly terminated expression - expected a constant or variable"), NULL);
  997. return false;
  998. }
  999. $token = $this->RDP_tokens[++$this->RDP_pos];
  1000. if ($token[2] == 'NOT' || $token[2] == 'BINARYOP')
  1001. {
  1002. switch ($token[0])
  1003. {
  1004. case '+':
  1005. case '-':
  1006. case '!':
  1007. if (!$this->RDP_EvaluatePrimaryExpression())
  1008. {
  1009. return false;
  1010. }
  1011. return $this->RDP_EvaluateUnary($token);
  1012. break;
  1013. default:
  1014. --$this->RDP_pos;
  1015. return $this->RDP_EvaluatePrimaryExpression();
  1016. }
  1017. }
  1018. else
  1019. {
  1020. --$this->RDP_pos;
  1021. return $this->RDP_EvaluatePrimaryExpression();
  1022. }
  1023. }
  1024. /**
  1025. * Returns array of al…

Large files files are truncated, but you can click here to view the full file