diff options
-rw-r--r-- | parser.php | 341 |
1 files changed, 178 insertions, 163 deletions
@@ -1,8 +1,23 @@ <?php +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see <http://www.gnu.org/licenses/>. + // Parser code for the Moodle Algebra question type // Moodle algebra question type class // Author: Roger Moore <rwmoore 'at' ualberta.ca> -// License: GNU Public License version 3 +// License: GNU Public License version 3. defined('MOODLE_INTERNAL') || die(); @@ -33,13 +48,13 @@ if (!function_exists('lcfirst')) { * @return -1 if $a is longer than $b, 0 if they are the same length and +1 if $a is shorter */ function qtype_algebra_parser_strlen_sort($a, $b) { - // Get the two string lengths once so we don't have to repeat the function call + // Get the two string lengths once so we don't have to repeat the function call. $alen = strlen($a); $blen = strlen($b); - // If the two lengths are equal return zero + // If the two lengths are equal return zero. if ($alen == $blen) return 0; - // Otherwise return +1 if a>b or -1 if a<b - return ($alen>$blen) ? -1 : +1; + // Otherwise return +1 if a > b or -1 if a < b. + return ($alen > $blen) ? -1 : +1; } @@ -87,15 +102,15 @@ class qtype_algebra_parser_term { // Create an empty array to store the arguments in $args = array(); // Handle zero argument terms differently by making the - // first 'argument' the value of the term itself + // first 'argument' the value of the term itself. if ($this->_nargs == 0) { $args[] = $this->_value; } else { - foreach($this->_arguments as $arg) { + foreach ($this->_arguments as $arg) { $args[] = $arg->$method(); } } - // Return the array of arguments + // Return the array of arguments. return $args; } @@ -108,13 +123,13 @@ class qtype_algebra_parser_term { * @return string input syntax format string of the expression */ public function str() { - // First check to see if the class has been given all the arguments + // First check to see if the class has been given all the arguments. $this->check_arguments(); - // Get an array of all the arguments except for the format string + // Get an array of all the arguments except for the format string. $args = $this->print_args('str'); - // Insert the format string at the front of the argument array + // Insert the format string at the front of the argument array. array_unshift($args, $this->_formats['str']); - // Call sprintf using the argument array as the arguments + // Call sprintf using the argument array as the arguments. return call_user_func_array('sprintf', $args); } @@ -129,13 +144,13 @@ class qtype_algebra_parser_term { * @return LaTeX format string of the expression */ public function tex() { - // First check to see if the class has been given all the arguments + // First check to see if the class has been given all the arguments. $this->check_arguments(); - // Get an array of all the arguments except for the format string + // Get an array of all the arguments except for the format string. $args = $this->print_args('tex'); - // Insert the format string at the front of the argument array + // Insert the format string at the front of the argument array. array_unshift($args, $this->_formats['tex']); - // Call sprintf using the argument array as the arguments + // Call sprintf using the argument array as the arguments. return call_user_func_array('sprintf', $args); } @@ -150,21 +165,21 @@ class qtype_algebra_parser_term { * @return SAGE format string of the expression */ public function sage() { - // First check to see if the class has been given all the arguments + // First check to see if the class has been given all the arguments. $this->check_arguments(); - // Get an array of all the arguments except for the format string + // Get an array of all the arguments except for the format string. $args = $this->print_args('sage'); // Insert the format string at the front of the argument array. First we // check to see if there is a format element called 'sage' if not then we - // default to the standard string format + // default to the standard string format. if (array_key_exists('sage', $this->_formats)) { - // Insert the sage format string at the front of the argument array + // Insert the sage format string at the front of the argument array. array_unshift($args, $this->_formats['sage']); } else { - // Insert the normal format string at the front of the argument array + // Insert the normal format string at the front of the argument array. array_unshift($args, $this->_formats['str']); } - // Call sprintf using the argument array as the arguments + // Call sprintf using the argument array as the arguments. return call_user_func_array('sprintf', $args); } @@ -263,16 +278,16 @@ class qtype_algebra_parser_term { * @return an array containing all the terms of the selected type keyed by their value */ public function collect(&$list, $type) { - // Add this class to the list if of the correct type + // Add this class to the list if of the correct type. if (is_a($this, $type)) { // Add a key to the array with the value of the term, this means // that multiple terms with the same value will overwrite each // other so only one will remain. $list[$this->_value] = 0; } - // Now loop over all the argument for this term (if any) and check them - foreach($this->_arguments as $arg) { - // Collect terms from the arguments as well + // Now loop over all the argument for this term (if any) and check them. + foreach ($this->_arguments as $arg) { + // Collect terms from the arguments as well. $arg->collect($list, $type); } } @@ -288,7 +303,7 @@ class qtype_algebra_parser_term { * @return true if the terms match, false otherwise */ public function equals($term) { - // Default method just checks to ensure that the Terms are both of the same type + // Default method just checks to ensure that the Terms are both of the same type. return is_a($term, get_class($this)); } @@ -305,13 +320,13 @@ class qtype_algebra_parser_term { * @return true if the expressions match, false otherwise */ public function equivalent($expr) { - // Check that the argument is also a term + // Check that the argument is also a term. if (!is_a($expr, 'qtype_algebra_parser_term')) { throw new Exception(get_string('badequivtype', 'qtype_algebra')); } - // Now check that this term is the same as the given term + // Now check that this term is the same as the given term. if (!$this->equals($expr)) { - // Terms are not equal immediately return false since the two do not match + // Terms are not equal immediately return false since the two do not match. return false; } // Now compare the arguments recursively... @@ -321,18 +336,18 @@ class qtype_algebra_parser_term { // because there are no arguments to check we are equivalent! return true; case 1: - // For one argument we also need to compare the argument of each term + // For one argument we also need to compare the argument of each term. return $this->_arguments[0]->equivalent($expr->_arguments[0]); case 2: // Now it gets interesting. First we compare the two arguments in the same // order and see what we get... if ($this->_arguments[0]->equivalent($expr->_arguments[0]) and $this->_arguments[1]->equivalent($expr->_arguments[1])) { - // Both arguments are equivalent so we have a match + // Both arguments are equivalent so we have a match. return true; } // Otherwise if the operator commutes we can see if the first argument matches - // the second argument and vice versa + // the second argument and vice versa. else if ($this->_commutes and $this->_arguments[0]->equivalent($expr->_arguments[1]) and $this->_arguments[1]->equivalent($expr->_arguments[0])) { return true; @@ -386,7 +401,7 @@ class qtype_algebra_parser_term { } else { echo "'>\n"; } - foreach($this->_arguments as $arg) { + foreach ($this->_arguments as $arg) { $arg->dump($params, $indent.' '); } } @@ -404,11 +419,11 @@ class qtype_algebra_parser_term { return '<Algebraic parser term of type \''.get_class($this).'\'>'; } - // Member variables - var $_value; // String of the actual term itself - var $_arguments = array(); // Array of arguments in class form - var $_formats; // Array of format strings - var $_nargs; // Number of arguments for this term + // Member variables. + var $_value; // String of the actual term itself. + var $_arguments = array(); // Array of arguments in class form. + var $_formats; // Array of format strings. + var $_nargs; // Number of arguments for this term. } /** @@ -452,11 +467,11 @@ class qtype_algebra_parser_nullterm extends qtype_algebra_parser_term { * @param $params array of the variable values to use */ public function evaluate($params) { - // Return something which is not a number + // Return something which is not a number. return acos(2.0); } - // Static class properties + // Static class properties. const NARGS = 0; private static $formats = array('str' => '', 'tex' => ''); @@ -483,10 +498,10 @@ class qtype_algebra_parser_number extends qtype_algebra_parser_term { public function __construct($text = '') { // Unfortunately PHP maths will only support a '.' as a decimal point and will not support // ',' as used in Danish, French etc. To allow for this we always convert any commas into - // decimal points before we parse the string + // decimal points before we parse the string. $text = str_replace(',', '.', $text); $this->_sign = ''; - // Now determine whether this is in exponent form or just a plain number + // Now determine whether this is in exponent form or just a plain number. if (preg_match('/([\.0-9]+)E([-+]?\d+)/', $text, $m)) { $this->_base = $m[1]; $this->_exp = $m[2]; @@ -508,7 +523,7 @@ class qtype_algebra_parser_number extends qtype_algebra_parser_term { * not have a variable or another number preceding it. */ public function set_negative() { - // Prepend a minus sign to both the base and total value strings + // Prepend a minus sign to both the base and total value strings. $this->_base = '-'.$this->_base; $this->_value = '-'.$this->_value; $this->_sign = '-'; @@ -524,7 +539,7 @@ class qtype_algebra_parser_number extends qtype_algebra_parser_term { * @return true if the terms match, false otherwise */ public function equals($expr) { - // Call the default method first to check type + // Call the default method first to check type. if (parent::equals($expr)) { return (float)$this->_value == (float)$expr->_value; } else { @@ -548,14 +563,14 @@ class qtype_algebra_parser_number extends qtype_algebra_parser_term { // decimal point (which we have to use internally because of the PHP math standard) // with the correct string from the language pack $base = str_replace('.', get_string('decimal', 'qtype_algebra'), $this->_base); - // Put the base part of the number into the argument array + // Put the base part of the number into the argument array. $args = array($base); // Check to see if we have an exponent... if ($this->_exp) { - // ...we do so add it to the argument array as well + // We do so add it to the argument array as well. $args[] = $this->_exp; } - // Return the list of arguments + // Return the list of arguments. return $args; } @@ -571,7 +586,7 @@ class qtype_algebra_parser_number extends qtype_algebra_parser_term { return doubleval($this->_value); } - // Static class properties + // Static class properties. const NARGS = 0; private static $formats = array('str' => '%s', 'tex' => '%s '); @@ -629,38 +644,38 @@ class qtype_algebra_parser_variable extends qtype_algebra_parser_term { * @param $text text matching the variable name */ public function __construct($text) { - // Create the array to store the regular expression matches in + // Create the array to store the regular expression matches in. $m = array(); - // Set the sign of the variable to be empty + // Set the sign of the variable to be empty. $this->_sign = ''; - // Try to match the text to a greek letter + // Try to match the text to a greek letter. if (preg_match('/('.implode('|', self::$greek).')/A', $text, $m)) { - // Take the base name of the variable to be the greek letter + // Take the base name of the variable to be the greek letter. $this->_base = $m[1]; - // Extract the remaining characters for use as the subscript + // Extract the remaining characters for use as the subscript. $this->_subscript = substr($text, strlen($m[1])); - // If the first letter of the subscript is an underscore then remove it + // If the first letter of the subscript is an underscore then remove it. if ($this->_subscript[0] == '_') { $this->_subscript = substr($this->_subscript, 1); } // Call the base class constructor with the variable text set to the combination of the - // base name and the subscript without an underscore between them + // base name and the subscript without an underscore between them. parent::__construct(self::NARGS, self::$formats['greek'], $this->_base.$this->_subscript); } // Otherwise we have a simple multi-letter variable name. Treat the fist letter as the base - // name and the rest as the subscript + // name and the rest as the subscript. else { - // Get the variable's base name + // Get the variable's base name. $this->_base = substr($text, 0, 1); - // Now set the subscript to the remaining letters + // Now set the subscript to the remaining letters. $this->_subscript = substr($text, 1); - // If the first letter of the subscript is an underscore then remove it + // If the first letter of the subscript is an underscore then remove it. if ($this->_subscript[0] == '_') { $this->_subscript = substr($this->_subscript, 1); } // Call the base class constructor with the variable text set to the combination of the - // base name and the subscript without an underscore between them + // base name and the subscript without an underscore between them. parent::__construct(self::NARGS, self::$formats['std'], $this->_base.$this->_subscript); } @@ -709,7 +724,7 @@ class qtype_algebra_parser_variable extends qtype_algebra_parser_term { if (array_key_exists($this->_value, $params)) { return $mult*doubleval($params[$this->_value]); } else { - // Found an indefined variable. Cannot evaluate numerically so throw exception + // Found an indefined variable. Cannot evaluate numerically so throw exception. throw new Exception(get_string('undefinedvariable', 'qtype_algebra', $this->_value)); } } @@ -724,7 +739,7 @@ class qtype_algebra_parser_variable extends qtype_algebra_parser_term { * @return true if the terms match, false otherwise */ public function equals($expr) { - // Call the default method first to check type + // Call the default method first to check type. if (parent::equals($expr)) { return $this->_value == $expr->_value and $this->_sign == $expr->_sign; } else { @@ -732,7 +747,7 @@ class qtype_algebra_parser_variable extends qtype_algebra_parser_term { } } - // Static class properties + // Static class properties. const NARGS = 0; private static $formats = array( 'greek' => array('str' => '%s%s%s', @@ -781,7 +796,7 @@ class qtype_algebra_parser_power extends qtype_algebra_parser_term { doubleval($this->_arguments[1]->evaluate($params))); } - // Static class properties + // Static class properties. const NARGS = 2; private static $formats = array( 'str' => '%s^%s', @@ -824,19 +839,19 @@ class qtype_algebra_parser_divide extends qtype_algebra_parser_term { */ public function evaluate($params) { $this->check_arguments(); - // Get the value we are trying to divide by + // Get the value we are trying to divide by. $divby = $this->_arguments[1]->evaluate($params); - // Check to see if this is zero + // Check to see if this is zero. if ($divby == 0) { // Check the sign of the other argument and use to determine whether we return - // plus or minus infinity + // plus or minus infinity. return INF*$this->_arguments[0]->evaluate($params); } else { return $this->_arguments[0]->evaluate($params)/$divby; } } - // Static class properties + // Static class properties. const NARGS = 2; private static $formats = array( 'str' => '%s/%s', @@ -882,13 +897,13 @@ class qtype_algebra_parser_multiply extends qtype_algebra_parser_term { */ public function set_arguments($args) { // First perform default argument setting method. This will generate - // an error if there is a problem with the number of arguments + // an error if there is a problem with the number of arguments. parent::set_arguments($args); - // Set the default explicit format + // Set the default explicit format. $this->_formats = $this->mformats['*']; // Only allow the implicit multipication if the second argument is either a // special, variable, function or bracket and not negative. In all other cases the operator must be - // explicitly written + // explicitly written. if (is_a($args[1], 'qtype_algebra_parser_bracket') or is_a($args[1], 'qtype_algebra_parser_variable') or is_a($args[1], 'qtype_algebra_parser_special') or @@ -898,7 +913,7 @@ class qtype_algebra_parser_multiply extends qtype_algebra_parser_term { } } // Check for one more special exemption: if the second argument is a power expression - // then we use the same criteria on the first argument of it + // then we use the same criteria on the first argument of it. if (is_a($args[1], 'qtype_algebra_parser_power')) { // Get the arguments from the power term. Note we do not check these since // power terms are parsed before multiplication ones and are required to @@ -933,7 +948,7 @@ class qtype_algebra_parser_multiply extends qtype_algebra_parser_term { $this->_arguments[1]->evaluate($params); } - // Static class properties + // Static class properties. const NARGS = 2; } @@ -976,7 +991,7 @@ class qtype_algebra_parser_add extends qtype_algebra_parser_term { $this->_arguments[1]->evaluate($params); } - // Static class properties + // Static class properties. const NARGS = 2; private static $formats = array( 'str' => '%s+%s', @@ -1023,7 +1038,7 @@ class qtype_algebra_parser_subtract extends qtype_algebra_parser_term { $this->_arguments[1]->evaluate($params); } - // Static class properties + // Static class properties. const NARGS = 2; private static $formats = array( 'str' => '%s-%s', @@ -1061,7 +1076,7 @@ class qtype_algebra_parser_special extends qtype_algebra_parser_term { * not have a variable or another number preceding it. */ public function set_negative() { - // Set the sign to be a '-' + // Set the sign to be a '-'. $this->_sign = '-'; } @@ -1114,7 +1129,7 @@ class qtype_algebra_parser_special extends qtype_algebra_parser_term { * @return true if the terms match, false otherwise */ public function equals($expr) { - // Call the default method first to check type + // Call the default method first to check type. if (parent::equals($expr)) { return $this->_value == $expr->_value and $this->_sign == $this->_sign; } else { @@ -1122,7 +1137,7 @@ class qtype_algebra_parser_special extends qtype_algebra_parser_term { } } - // Static class properties + // Static class properties. const NARGS = 0; private static $formats = array( 'pi' => array( 'str' => '%spi', @@ -1172,7 +1187,7 @@ class qtype_algebra_parser_function extends qtype_algebra_parser_term { * not have a variable or another number preceding it e.g. 3*-sin(x) */ public function set_negative() { - // Set the sign to be a '-' + // Set the sign to be a '-'. $this->_sign = '-'; } @@ -1190,25 +1205,25 @@ class qtype_algebra_parser_function extends qtype_algebra_parser_term { throw new Exception(get_string('badfuncargs', 'qtype_algebra', $this->_value)); } if (!is_a($args[0], 'qtype_algebra_parser_bracket')) { - // Check to see if this function requires a special bracket + // Check to see if this function requires a special bracket. if (in_array($this->_value, self::$bracketmap)) { $b = new qtype_algebra_parser_bracket('<'); } - // Does not require special brackets so create normal ones + // Does not require special brackets so create normal ones. else { $b = new qtype_algebra_parser_bracket('('); } $b->set_arguments($args); $this->_arguments = array($b); } - // First term already a bracket + // First term already a bracket. else { - // Check to see if we need a special bracket + // Check to see if we need a special bracket. if (in_array($this->_value, self::$bracketmap)) { // Make the bracket special $args[0]->make_special(); } - // Set the arguments to the given type + // Set the arguments to the given type. $this->_arguments = $args; } } @@ -1223,7 +1238,7 @@ class qtype_algebra_parser_function extends qtype_algebra_parser_term { * @return array of the arguments that, with a format string, can be passed to sprintf */ public function print_args($method) { - // First ensure that there are the correct number of arguments + // First ensure that there are the correct number of arguments. $this->check_arguments(); return array($this->_sign, $this->_arguments[0]->$method()); } @@ -1240,15 +1255,15 @@ class qtype_algebra_parser_function extends qtype_algebra_parser_term { * @return the numerical value of the term given the provided values for the variables */ public function evaluate($params) { - // First ensure that there are the correct number of arguments + // First ensure that there are the correct number of arguments. $this->check_arguments(); - // Get the correct sign to multiply the value by + // Get the correct sign to multiply the value by. if ($this->_sign == '-') { $mult = -1; } else { $mult = 1; } - // Check to see if there is an entry to map the function name to a PHP function + // Check to see if there is an entry to map the function name to a PHP function. if (array_key_exists($this->_value, self::$fnmap)) { $func = self::$fnmap[$this->_value]; return $mult*$func($this->_arguments[0]->evaluate($params)); @@ -1270,7 +1285,7 @@ class qtype_algebra_parser_function extends qtype_algebra_parser_term { * @return true if the terms match, false otherwise */ public function equals($expr) { - // Call the default method first to check type + // Call the default method first to check type. if (parent::equals($expr)) { return $this->_value == $expr->_value and $this->_sign == $this->_sign; } else { @@ -1278,7 +1293,7 @@ class qtype_algebra_parser_function extends qtype_algebra_parser_term { } } - // Static class properties + // Static class properties. const NARGS = 1; public static $fnmap = array ('ln' => 'log', 'log' => 'log10' @@ -1349,7 +1364,7 @@ class qtype_algebra_parser_bracket extends qtype_algebra_parser_term { } public function set_negative() { - // Set the sign to be a '-' + // Set the sign to be a '-'. $this->_sign = '-'; } @@ -1362,15 +1377,15 @@ class qtype_algebra_parser_bracket extends qtype_algebra_parser_term { public function make_special() { $this->_open = '<'; $this->_close = '>'; - // Call the base class constructor as if this were a new instance of the bracket + // Call the base class constructor as if this were a new instance of the bracket. parent::__construct(self::NARGS, self::$formats['<'], '<'); } - // Member variables + // Member variables. var $_open = '('; var $_close = ')'; - // Static class properties + // Static class properties. const NARGS = 1; private static $formats = array( '(' => array('str' => '(%s)', @@ -1394,7 +1409,7 @@ class qtype_algebra_parser_bracket extends qtype_algebra_parser_term { * instances of the correct subclass to handle them. */ class qtype_algebra_parser { - // Special constants which the parser will understand + // Special constants which the parser will understand. public static $specials = array ( 'pi', 'e' @@ -1415,7 +1430,7 @@ class qtype_algebra_parser { ); // Array to define the priority of the different operations. The parser implements the standard BODMAS priority: - // brackets, order (power), division, mulitplication, addition, subtraction + // brackets, order (power), division, mulitplication, addition, subtraction. private static $priority = array ( array('qtype_algebra_parser_power'), array('qtype_algebra_parser_function'), @@ -1423,15 +1438,15 @@ class qtype_algebra_parser { array('qtype_algebra_parser_add', 'qtype_algebra_parser_subtract') ); - // Regular experssion to match an open bracket + // Regular experssion to match an open bracket. private static $OPENB = '/[\{\(\[]/A'; - // Regular experssion to match a close bracket + // Regular experssion to match a close bracket. private static $CLOSEB = '/[\}\)\]]/A'; - // Regular expression to match a plain float or integer number without exponent + // Regular expression to match a plain float or integer number without exponent. private static $PLAIN_NUMBER = '(([0-9]+(\.|,)[0-9]*)|([0-9]+)|((\.|,)[0-9]+))'; - // Regular expression to match a float or integer number with an exponent + // Regular expression to match a float or integer number with an exponent. private static $EXP_NUMBER = '(([0-9]+(\.|,)[0-9]*)|([0-9]+)|((\.|,)[0-9]+))E([-+]?\d+)'; - // Array to associate close brackets with the correct open bracket type + // Array to associate close brackets with the correct open bracket type. private static $BRACKET_MAP = array(')' => '(', ']' => '[', '}' => '{'); /** @@ -1474,119 +1489,119 @@ class qtype_algebra_parser { * @return top term of the parsed expression */ public function parse($text, $variables = array(), $undecvars = false) { - // Create a regular expression to match the known variables if an array is specified + // Create a regular expression to match the known variables if an array is specified. if (!empty($variables)) { - // Create an empty array to store the list of extra regular expressions to match + // Create an empty array to store the list of extra regular expressions to match. $reextra = array(); - // Loop over all the variable names we are given - foreach($variables as $var) { - // Create a temporary varible term using the current name + // Loop over all the variable names we are given. + foreach ($variables as $var) { + // Create a temporary varible term using the current name. $tmpvar = new qtype_algebra_parser_variable($var); // If the variable name has a subscript then create a new regular expression to - // search for which includes an underscore + // search for which includes an underscore. if (!empty($tmpvar->_subscript)) { $reextra[] = $tmpvar->_base.'_'.$tmpvar->_subscript; } } - // Merge the variable name array with the array of extra regular expressions to match + // Merge the variable name array with the array of extra regular expressions to match. $variables = array_merge($variables, $reextra); // Sort the array in order of increasing variable length in order to prevent 'x1' matching // a variable 'x' before 'x1'. Do this using a helper function, which will compare two // strings using their length only, and use this with the usort function. usort($variables, 'qtype_algebra_parser_strlen_sort'); - // Generate a single regular expression which will match both all known variables + // Generate a single regular expression which will match both all known variables. $revar = '/('.implode('|', $variables).')/A'; } else { $revar = ''; } $i = 0; - // Create an array to store the parse tree + // Create an array to store the parse tree. $tree = array(); // Create an array to act as a temporary storage stack. This stack is used to - // push higher levels of the parse tree as it is assembled from the expression + // push higher levels of the parse tree as it is assembled from the expression. $stack = array(); - // Array used to store the match results from regular expression searches + // Array used to store the match results from regular expression searches. $m = array(); // Loop over the expression string moving along it using the offset variable $i while - // there are still characters left to parse - while($i<strlen($text)) { + // there are still characters left to parse. + while($i < strlen($text)) { // Match any white space at the start of the string and 'remove' it by advancing // the pointer by the length of the string matching the regular expression white - // space pattern + // space pattern. if (preg_match('/\s+/A', substr($text, $i), $m)) { - $i+ = strlen($m[0]); + $i += strlen($m[0]); // Return to the start of the loop in case this was white space characters at - // the end of the string + // the end of the string. continue; } // Since we don't have any white space the first thing we look for (top priority) - // are open brackets + // are open brackets. if (preg_match(self::$OPENB, substr($text, $i), $m)) { - // Check for a non-operator and if one is found assume implicit multiplication + // Check for a non-operator and if one is found assume implicit multiplication. if (count($tree)>0 and (is_array($tree[count($tree)-1]) or (is_object($tree[count($tree)-1]) and $tree[count($tree)-1]->n_args() == 0))) { // Make the implicit assumption explicit by adding an appropriate - // multiplication operator + // multiplication operator. array_push($tree, new qtype_algebra_parser_multiply('*')); } - // Push the current parse tree onto the stack + // Push the current parse tree onto the stack. array_push($stack, $tree); - // Create a new parse tree starting with a bracket term + // Create a new parse tree starting with a bracket term. $tree = array(new qtype_algebra_parser_bracket($m[0])); - // Increment the string pointer by the length of the string that was matched - $i+ = strlen($m[0]); - // Return to the start of the loop + // Increment the string pointer by the length of the string that was matched. + $i += strlen($m[0]); + // Return to the start of the loop. continue; } - // Now see if we have a close bracket here + // Now see if we have a close bracket here. if (preg_match(self::$CLOSEB, substr($text, $i), $m)) { - // First check that the current parse tree has at least one term + // First check that the current parse tree has at least one term. if (count($tree) == 0) { throw new Exception(get_string('badclosebracket', 'qtype_algebra')); } - // Now check that the current tree started with a bracket + // Now check that the current tree started with a bracket. if (!is_a($tree[0], 'qtype_algebra_parser_bracket')) { throw new Exception(get_string('mismatchedcloseb', 'qtype_algebra')); } - // Check that the open and close bracket are of the same type + // Check that the open and close bracket are of the same type. else if ($tree[0]->_value != self::$BRACKET_MAP[$m[0]]) { throw new Exception(get_string('mismatchedbracket', 'qtype_algebra', $tree[0]->_value.$m[0])); } - // Append the current tree to the tree one level up on the stack + // Append the current tree to the tree one level up on the stack. array_push($stack[count($stack)-1], $tree); // The new tree is the lowest level tree on the stack so we - // pop the new tree off the stack + // pop the new tree off the stack. $tree = array_pop($stack); - $i+ = strlen($m[0]); + $i += strlen($m[0]); continue; } - // If a list of predefined variables was given to the method then check for them here + // If a list of predefined variables was given to the method then check for them here. if (!empty($revar) and preg_match($revar, substr($text, $i), $m)) { // Check for a zero argument term or brackets preceding the variable and if there is one then - // add the implicit multiplication operation + // add the implicit multiplication operation. if (count($tree)>0 and (is_array($tree[count($tree)-1]) or $tree[count($tree)-1]->n_args() == 0)) { array_push($tree, new qtype_algebra_parser_multiply('*')); } - // Increment the string index by the length of the variable's name - $i+ = strlen($m[0]); - // Push a new variable term onto the parse tree + // Increment the string index by the length of the variable's name. + $i += strlen($m[0]); + // Push a new variable term onto the parse tree. array_push($tree, new qtype_algebra_parser_variable($m[0])); continue; } // Here we have not found any open or close brackets or known variables so we can - // parse the string for a normal token - foreach($this->_tokens as $token) { + // parse the string for a normal token. + foreach ($this->_tokens as $token) { //echo 'Looking for token ', $token[1], "\n"; if (preg_match($token[0], substr($text, $i), $m)) { //echo 'Found a ', $token[1], "!\n"; // Check for a variable and throw an exception if undeclared variables are - // not allowed and a list of defined variables was passed + // not allowed and a list of defined variables was passed. if (!empty($revar) and !$undecvars and $token[1] == 'qtype_algebra_parser_variable') { throw new Exception(get_string('undeclaredvar', 'qtype_algebra', $m[0])); } // Check for a zero argument term preceding a variable, function or special and then - // add the implicit multiplication + // add the implicit multiplication. if (count($tree)>0 and ($token[1] == 'qtype_algebra_parser_variable' or $token[1] == 'qtype_algebra_parser_function' or $token[1] == 'qtype_algebra_parser_special') @@ -1594,15 +1609,15 @@ class qtype_algebra_parser { $tree[count($tree)-1]->n_args() == 0)) { array_push($tree, new qtype_algebra_parser_multiply('*')); } - $i+ = strlen($m[0]); + $i += strlen($m[0]); array_push($tree, new $token[1]($m[0])); continue 2; } } throw new Exception(get_string('unknownterm', 'qtype_algebra', substr($text, $i))); - } // end while loop over tokens + } // End while loop over tokens. // If all the open brackets have been closed then the stack will be empty and the - // tree will contain the entire parsed expression + // tree will contain the entire parsed expression. if (count($stack)>0) { throw new Exception(get_string('mismatchedopenb', 'qtype_algebra')); } @@ -1623,7 +1638,7 @@ class qtype_algebra_parser { */ public function interpret($tree) { // First check to see if we are passed anything at all. If not then simply - // return a qtype_algebra_parser_nullterm + // return a qtype_algebra_parser_nullterm. if (count($tree) == 0) { return new qtype_algebra_parser_nullterm; } @@ -1639,21 +1654,21 @@ class qtype_algebra_parser { } // Next we loop over the tree and look for arrays. These represent // brackets inside our tree and so we need to process them first. - for($i = 0;$i<count($tree);$i++) { + for ($i = 0;$i < count($tree);$i++) { // Check for a list type if we find one then replace - // it with the interpreted term + // it with the interpreted term. if (is_array($tree[$i])) { $tree[$i] = $this->interpret($tree[$i]); } } // The next job is to check the subtraction operations to determine whether they are - // really subtraction operations or whether they are minus signs for negative numbers + // really subtraction operations or whether they are minus signs for negative numbers. $toremove = array(); - for($i = 0;$i<count($tree);$i++) { - // Check that this element is an addition or subtraction operator + for ($i = 0;$i < count($tree);$i++) { + // Check that this element is an addition or subtraction operator. if (is_a($tree[$i], 'qtype_algebra_parser_subtract') or is_a($tree[$i], 'qtype_algebra_parser_add')) { // Check whether the precedding argument (if there is one) is a number or - // a variable. In either case this is a addition/subtraction operation so we continue + // a variable. In either case this is a addition/subtraction operation so we continue. if ($i>0 and (is_a($tree[$i-1], 'qtype_algebra_parser_variable') or is_a($tree[$i-1], 'qtype_algebra_parser_number') or is_a($tree[$i-1], 'qtype_algebra_parser_bracket'))) { @@ -1665,28 +1680,28 @@ class qtype_algebra_parser { if ($i == (count($tree)-1) or !method_exists($tree[$i+1], 'set_negative')) { throw new Exception(get_string('illegalplusminus', 'qtype_algebra')); } - // If we have a subtract operation then we need to make the following number negative + // If we have a subtract operation then we need to make the following number negative. if (is_a($tree[$i], 'qtype_algebra_parser_subtract')) { - // Set the number to be negative + // Set the number to be negative. $tree[$i+1]->set_negative(); } - // Add the term to the removal list + // Add the term to the removal list. $toremove[$i] = 1; } } } - // Remove the elements from the tree who's keys are found in the removal list + // Remove the elements from the tree who's keys are found in the removal list. $tree = array_diff_key($tree, $toremove); - // Re-key the tree array so that the keys are sequential + // Re-key the tree array so that the keys are sequential. $tree = array_values($tree); - foreach(self::$priority as $ops) { + foreach (self::$priority as $ops) { $i = 0; //echo 'Looking for ', $ops, "\n"; - while($i<count($tree)) { + while($i < count($tree)) { if (in_array(get_class($tree[$i]), $ops)) { //echo 'Found a ', get_class($tree[$i]), "\n"; if ($tree[$i]->n_args() == 1) { - if (($i+1)<count($tree)) { + if (($i+1) < count($tree)) { $tree[$i]->set_arguments(array_splice($tree, $i+1, 1)); $i++; continue; @@ -1694,7 +1709,7 @@ class qtype_algebra_parser { throw new Exception(get_string('missingonearg', 'qtype_algebra', $op)); } } elseif ($tree[$i]->n_args() == 2) { - if ($i>0 and $i<(count($tree)-1)) { + if ($i > 0 and $i < (count($tree)-1)) { $tree[$i]->set_arguments(array($tree[$i-1], $tree[$i+1])); array_splice($tree, $i+1, 1); @@ -1710,7 +1725,7 @@ class qtype_algebra_parser { } } // If there are no terms in the parse tree then we were passed an empty string - // in which case we create a null term and return it + // in which case we create a null term and return it. if (count($tree) == 0) { return new qtype_algebra_parser_nullterm; } else if (count($tree)!= 1) { @@ -1726,7 +1741,7 @@ class qtype_algebra_parser { } } -// Sort static arrays once here by inverse string length +// Sort static arrays once here by inverse string length. usort(qtype_algebra_parser_variable::$greek, 'qtype_algebra_parser_strlen_sort'); usort(qtype_algebra_parser::$functions, 'qtype_algebra_parser_strlen_sort'); |