/** * Check for strings within the casstring. This is only used in the "fobidden words" option. * @return bool|string true if an element of array is found in the casstring. */ public function check_external_forbidden_words_literal($keywords) { if (null === $this->valid) { $this->validate(); } // Deal with escaped commas. $keywords = str_replace('\\,', 'COMMA_TAG', $keywords); $keywords = explode(',', $keywords); // Replace lists of keywords with their actual values. $kws = array(); foreach ($keywords as $val) { $kw = trim(strtolower($val)); if (array_key_exists($kw, self::$keywordlists)) { $kws = array_merge($kws, self::$keywordlists[$kw]); } else { if ('COMMA_TAG' === $val) { $kws[] = ','; } else { $kws[] = trim($val); // This test is case sensitive, but ignores surrounding whitespace. } } } $found = false; foreach ($kws as $key) { if (!(false === strpos($this->rawcasstring, $key))) { $found = true; $this->valid = false; $this->add_error(stack_string('stackCas_forbiddenWord', array('forbid' => stack_maxima_format_casstring($key)))); } } return $found; }
/** * Validate any attempts at this question. * * @param array $response the student response to the question. * @param stack_options $options CAS options to use when validating. * @param string $teacheranswer the teachers answer as a string representation of the evaluated expression. * @return stack_input_state represents the current state of the input. */ public function validate_student_response($response, $options, $teacheranswer, $forbiddenkeys) { if (!is_a($options, 'stack_options')) { throw new stack_exception('stack_input: validate_student_response: options not of class stack_options'); } $localoptions = clone $options; // The validation field should always come back through as a single RAW Maxima expression for each input. if (array_key_exists($this->name . '_val', $response)) { $validator = $response[$this->name . '_val']; } else { $validator = ''; } $contents = $this->response_to_contents($response); if (array() == $contents or $this->is_blank_response($contents)) { return new stack_input_state(self::BLANK, array(), '', '', '', '', ''); } // This method actually validates any CAS strings etc. list($valid, $errors, $modifiedcontents) = $this->validate_contents($contents, $forbiddenkeys); // If we can't get a "displayed value" back from the CAS, show the student their original expression. $display = stack_maxima_format_casstring($this->contents_to_maxima($contents)); $interpretedanswer = $this->contents_to_maxima($modifiedcontents); $answer = new stack_cas_casstring($interpretedanswer); $lvarsdisp = ''; // Send the string to the CAS. if ($valid) { if (!$this->get_parameter('sameType')) { $teacheranswer = null; } $singlevarchars = false; if (2 == $this->get_parameter('insertStars', 0)) { $singlevarchars = true; } // Generate an expression from which we extract the list of variables in the student's answer. $lvars = new stack_cas_casstring('ev(listofvars(' . $interpretedanswer . '),simp)'); $lvars->get_valid('t', $this->get_parameter('strictSyntax', true), $this->get_parameter('insertStars', 0), $this->get_parameter('allowWords', '')); $answer->set_cas_validation_casstring($this->name, $this->get_parameter('forbidFloats', false), $this->get_parameter('lowestTerms', false), $singlevarchars, $teacheranswer, $this->get_parameter('allowWords', '')); $localoptions->set_option('simplify', false); $session = new stack_cas_session(array($answer, $lvars), $localoptions, 0); $session->instantiate(); $session = $session->get_session(); $answer = $session[0]; $lvars = $session[1]; $errors = stack_maxima_translate($answer->get_errors()); if ('' != $errors) { $valid = false; } if ('' == $answer->get_value()) { $valid = false; } else { $display = '\\[ ' . $answer->get_display() . ' \\]'; $interpretedanswer = $answer->get_value(); if (!($lvars->get_value() == '[]')) { $lvarsdisp = '\\( ' . $lvars->get_display() . '\\) '; } } } $note = $answer->get_answernote(); // Answers may not contain the ? character. CAS-strings may, but answers may not. // It is very useful for teachers to be able to add in syntax hints. if (!(strpos($interpretedanswer, '?') === false)) { $valid = false; $errors .= stack_string('qm_error'); } if (!$valid) { $status = self::INVALID; } else { if ($this->get_parameter('mustVerify', true) && $validator != $this->contents_to_maxima($contents)) { $status = self::VALID; } else { $status = self::SCORE; } } return new stack_input_state($status, $contents, $interpretedanswer, $display, $errors, $note, $lvarsdisp); }
public function instantiate() { if (null === $this->valid) { $this->validate(); } if (!$this->valid) { return false; } // Lazy instantiation - only do this once... // Empty session. Nothing to do. if ($this->instantiated || null === $this->session) { return true; } $connection = stack_connection_helper::make(); $results = $connection->compute($this->construct_maxima_command()); $this->debuginfo = $connection->get_debuginfo(); // Now put the information back into the correct slots. $session = $this->session; $newsession = array(); $newerrors = ''; $allfail = true; $i = 0; // We loop over each entry in the session, not over the result. // This way we can add an error for missing values. foreach ($session as $cs) { $gotvalue = false; if ('' == $cs->get_key()) { $key = 'dumvar' . $i; } else { $key = $cs->get_key(); } if (array_key_exists($i, $results)) { $allfail = false; // We at least got one result back from the CAS! $result = $results["{$i}"]; // GOCHA! Results have string represenations of numbers, not int.... if ('' != $result['error'] and false === strstr($result['error'], 'clipped')) { $cs->add_errors($result['error']); $cs->decode_maxima_errors($result['error']); $newerrors .= stack_maxima_format_casstring($cs->get_raw_casstring()); $newerrors .= ' ' . stack_string("stackCas_CASErrorCaused") . ' ' . $result['error'] . ' '; } if (array_key_exists('value', $result)) { $val = str_replace('QMCHAR', '?', $result['value']); $cs->set_value($val); $gotvalue = true; } else { $cs->add_errors(stack_string("stackCas_failedReturnOne")); } if (array_key_exists('display', $result)) { // Need to add this in here also because strings may contain question mark characters. $disp = str_replace('QMCHAR', '?', $result['display']); $cs->set_display($disp); } if (array_key_exists('valid', $result)) { $cs->set_valid($result['valid']); } if (array_key_exists('answernote', $result)) { $cs->set_answernote($result['answernote']); } if (array_key_exists('feedback', $result)) { $cs->set_feedback($result['feedback']); } } else { if (!$gotvalue) { $errstr = stack_string("stackCas_failedReturn") . ' ' . stack_maxima_format_casstring($cs->get_raw_casstring()); $cs->add_errors($errstr); $cs->set_answernote('CASFailedReturn'); $newerrors .= $errstr; } } $newsession[] = $cs; $i++; } $this->session = $newsession; if ('' != $newerrors) { $this->errors .= '<span class="error">' . stack_string('stackCas_CASError') . '</span>' . $newerrors; } if ($allfail) { $this->errors = '<span class="error">' . stack_string('stackCas_allFailed') . '</span>'; } $this->instantiated = true; }
public static function run_test($test) { // Note: What we would really like to do is the following. // $el = stack_input_factory::make('algebraic', 'sans1', 'x'); // $el->set_parameter('insertStars', 1); // $el->set_parameter('strictSyntax', false); // $el->set_parameter('sameType', false); // $cs = $el->validate_student_response($test->rawstring); // However, we want to pull apart the bits to expose where the various errors occur. $cs = new stack_cas_casstring($test->rawstring); $cs->get_valid('s', false, 1); $cs->set_cas_validation_casstring('sans1', true, true, false, null); $phpvalid = $cs->get_valid(); if ($phpvalid) { // Trim off stack_validate_typeless([..], true, true). $phpcasstring = $cs->get_casstring(); $phpcasstring = substr($phpcasstring, 25); $phpcasstring = substr($phpcasstring, 0, strlen($phpcasstring) - 12); $outputphpcasstring = $phpcasstring; } else { $phpcasstring = ''; $outputphpcasstring = 'N/A...'; } $errors = $cs->get_errors(); $passed = true; if ('php_true' === $test->phpvalid) { $expected = true; } else { $expected = false; } if ($phpvalid != $expected) { $passed = false; $errors .= ' ' . stack_string('phpvalidatemismatch'); } if ($phpvalid && $phpcasstring != $test->phpcasstring) { $passed = false; $errors .= ' ' . stack_maxima_format_casstring($phpcasstring) . ' \\(\\neq \\) ' . stack_maxima_format_casstring($test->phpcasstring); } $casvalid = ''; $caserrors = ''; $casvalue = ''; $casdisplay = ''; if ($cs->get_valid()) { $options = new stack_options(); $options->set_option('simplify', false); $session = new stack_cas_session(array($cs), $options, 0); $session->instantiate(); $session = $session->get_session(); $cs = $session[0]; $caserrors = stack_maxima_translate($cs->get_errors()); $casvalue = stack_maxima_format_casstring($cs->get_value()); if ('cas_true' == $test->casvalid) { $casexpected = true; } else { $casexpected = false; } if ('' == $cs->get_value()) { $casvalid = false; } else { $casvalid = true; } if ($casexpected != $casvalid) { $passed = false; $caserrors .= ' ' . stack_string('casvalidatemismatch'); } $casdisplay = $cs->get_display(); } $answernote = $cs->get_answernote(); if ($answernote != $test->ansnotes) { $passed = false; $errors .= ' ' . stack_string('ansnotemismatch'); } return array($passed, $phpvalid, $phpcasstring, $errors, $casvalid, $caserrors, $casdisplay, $casvalue, $answernote); }