Skip to content
Snippets Groups Projects
Commit 0f9e4f55 authored by Matti Harjula's avatar Matti Harjula
Browse files

Fix candidate for #824. Not ready! Currently breaks CASText simplification control.

parent a89b1ecd
No related branches found
No related tags found
No related merge requests found
...@@ -173,8 +173,8 @@ if ($only === false) { ...@@ -173,8 +173,8 @@ if ($only === false) {
'letToken' => stack_string('equiv_LET'))); 'letToken' => stack_string('equiv_LET')));
} }
$pipeline = stack_parsing_rule_factory::get_filter_pipeline(array('996_call_modification', '998_security'), $pipeline = stack_parsing_rule_factory::get_filter_pipeline(array('995_ev_modification', '996_call_modification', '998_security'),
array('998_security' => array('security' => 's')), true); array('998_security' => array('security' => 's'), '995_ev_modification' => ['flags' => false]), true);
check_filter($freshast, $pipeline, new stack_cas_security(false), 'core + security(s)'); check_filter($freshast, $pipeline, new stack_cas_security(false), 'core + security(s)');
cli_heading('= core + security(t) + strict ='); cli_heading('= core + security(t) + strict =');
...@@ -186,7 +186,7 @@ if ($only === false) { ...@@ -186,7 +186,7 @@ if ($only === false) {
'letToken' => stack_string('equiv_LET'))); 'letToken' => stack_string('equiv_LET')));
} }
$pipeline = stack_parsing_rule_factory::get_filter_pipeline(array('996_call_modification', '998_security', '999_strict'), $pipeline = stack_parsing_rule_factory::get_filter_pipeline(array('995_ev_modification', '996_call_modification', '998_security', '999_strict'),
array('998_security' => array('security' => 't')), true); array('998_security' => array('security' => 't'), '995_ev_modification' => ['flags' => true]), true);
check_filter($freshast, $pipeline, new stack_cas_security(false), 'core + security(t) + strict'); check_filter($freshast, $pipeline, new stack_cas_security(false), 'core + security(t) + strict');
} }
...@@ -200,11 +200,12 @@ class stack_ast_container_silent implements cas_evaluatable { ...@@ -200,11 +200,12 @@ class stack_ast_container_silent implements cas_evaluatable {
} }
// As we take no filter options for teachers sourced stuff lets build them from scratch. // As we take no filter options for teachers sourced stuff lets build them from scratch.
$filteroptions = array('998_security' => array('security' => 't')); $filteroptions = array('998_security' => ['security' => 't'], '995_ev_modification' => ['flags' => true]);
// Get the filter pipeline. Now we only want the core filtters and // Get the filter pipeline. Now we only want the core filtters and
// append the strict syntax check to the end. // append the strict syntax check to the end.
$pipeline = stack_parsing_rule_factory::get_filter_pipeline(array('996_call_modification', '998_security', $pipeline = stack_parsing_rule_factory::get_filter_pipeline(array(
'995_ev_modification', '996_call_modification', '998_security',
'999_strict'), $filteroptions, true); '999_strict'), $filteroptions, true);
if ($ast !== null) { if ($ast !== null) {
...@@ -232,8 +233,10 @@ class stack_ast_container_silent implements cas_evaluatable { ...@@ -232,8 +233,10 @@ class stack_ast_container_silent implements cas_evaluatable {
$errors = array(); $errors = array();
$answernotes = array(); $answernotes = array();
$filteroptions = array('998_security' => array('security' => 't')); $filteroptions = array('998_security' => ['security' => 't']);
$pipeline = stack_parsing_rule_factory::get_filter_pipeline(array('998_security', '999_strict'), $filteroptions, true);
$pipeline = stack_parsing_rule_factory::get_filter_pipeline(array('998_security',
'999_strict'), $filteroptions, true);
$ast = $pipeline->filter($ast, $errors, $answernotes, $securitymodel); $ast = $pipeline->filter($ast, $errors, $answernotes, $securitymodel);
$astc = new static; $astc = new static;
......
...@@ -329,9 +329,11 @@ class stack_cas_keyval { ...@@ -329,9 +329,11 @@ class stack_cas_keyval {
$errors = []; $errors = [];
$answernotes = []; $answernotes = [];
$filteroptions = ['998_security' => ['security' => 't'], $filteroptions = ['998_security' => ['security' => 't'],
'601_castext' => ['context' => $contextname, 'errclass' => $this->errclass]]; '601_castext' => ['context' => $contextname, 'errclass' => $this->errclass],
'995_ev_modification' => ['flags' => true]];
$pipeline = stack_parsing_rule_factory::get_filter_pipeline(['601_castext', $pipeline = stack_parsing_rule_factory::get_filter_pipeline(['601_castext',
'602_castext_simplifier', '680_gcl_sconcat', '996_call_modification', '998_security', '999_strict'], '602_castext_simplifier', '680_gcl_sconcat', '995_ev_modification',
'996_call_modification', '998_security', '999_strict'],
$filteroptions, true); $filteroptions, true);
$tostringparams = ['nosemicolon' => true, 'pmchar' => 1]; $tostringparams = ['nosemicolon' => true, 'pmchar' => 1];
$securitymodel = $this->security; $securitymodel = $this->security;
......
<?php
// This file is part of Stack - https://stack.maths.ed.ac.uk
//
// Stack 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.
//
// Stack 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 Stack. If not, see <http://www.gnu.org/licenses/>.
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/filter.interface.php');
require_once(__DIR__ . '/996_call_modification.filter.php');
/**
* AST filter that rewrites calls to ev in such a way that they can deal
* with the security system. Also rewrites evaluation flags if they are
* in play.
*/
class stack_ast_filter_995_ev_modification implements stack_cas_astfilter_parametric {
// Unique counter for temp vars.
public static $TMPVARCOUNT = 1;
// Whether to rewrite evaluation flags. Don't do for students.
private $flags = false;
public function set_filter_parameters(array $parameters) {
$this->flags = isset($parameters['flags']) ? $parameters['flags'] : false;
}
public function filter(MP_Node $ast, array &$errors, array &$answernotes, stack_cas_security $identifierrules): MP_Node {
$process = function($node) {
if ($this->flags && $node instanceof MP_Statement &&
$node->flags !== null && count($node->flags) > 0) {
$fun = new MP_FunctionCall(new MP_Identifier('ev'), [$node->statement]);
if ($node->statement instanceof MP_Operation && $node->statement->op === ':') {
$fun->arguments[0] = $node->statement->rhs;
$node->statement->rhs = $fun;
}
foreach ($node->flags as $flag) {
$fun->arguments[] = new MP_Operation('=', $flag->name, $flag->value);
}
$node->flags = [];
return false;
}
if ($node instanceof MP_FunctionCall && $node->name instanceof MP_Atom &&
$node->name->value === 'ev') {
$payload = $node->arguments[0];
$tc = $payload->type_count();
// Do not touch these, indempotent behaviour required.
if (isset($tc['funs'][stack_ast_filter_996_call_modification::EXPCHECK])) {
unset($tc['funs'][stack_ast_filter_996_call_modification::EXPCHECK]);
}
if (count($tc['funs']) > 0) {
// Complex `ev`. As in iss824.
$id = '%_ev_tmp_' . self::$TMPVARCOUNT;
self::$TMPVARCOUNT = self::$TMPVARCOUNT + 1;
$id2 = '%_ev_tmp_' . self::$TMPVARCOUNT;
self::$TMPVARCOUNT = self::$TMPVARCOUNT + 1;
$replacement = new MP_Group([new MP_Operation(':', new MP_Identifier($id2), new MP_Identifier('simp')), new MP_Operation(':', new MP_Identifier('simp'), new MP_Boolean(false)), new MP_Operation(':', new MP_Identifier($id), $payload), new MP_Operation(':', new MP_Identifier('simp'), new MP_Identifier($id2)), $node]);
$node->arguments[0] = new MP_Identifier($id);
$node->parentnode->replace($node, $replacement);
return false;
}
}
return true;
};
// @codingStandardsIgnoreStart
while (!$ast->callbackRecurse($process)) { }
// @codingStandardsIgnoreEnd
return $ast;
}
}
...@@ -62,6 +62,7 @@ require_once(__DIR__ . '/802_singleton_units.filter.php'); ...@@ -62,6 +62,7 @@ require_once(__DIR__ . '/802_singleton_units.filter.php');
require_once(__DIR__ . '/910_inert_float_for_display.filter.php'); require_once(__DIR__ . '/910_inert_float_for_display.filter.php');
require_once(__DIR__ . '/990_no_fixing_spaces.filter.php'); require_once(__DIR__ . '/990_no_fixing_spaces.filter.php');
require_once(__DIR__ . '/991_no_fixing_stars.filter.php'); require_once(__DIR__ . '/991_no_fixing_stars.filter.php');
require_once(__DIR__ . '/995_ev_modification.filter.php');
require_once(__DIR__ . '/996_call_modification.filter.php'); require_once(__DIR__ . '/996_call_modification.filter.php');
require_once(__DIR__ . '/997_string_security.filter.php'); require_once(__DIR__ . '/997_string_security.filter.php');
require_once(__DIR__ . '/998_security.filter.php'); require_once(__DIR__ . '/998_security.filter.php');
...@@ -166,6 +167,8 @@ class stack_parsing_rule_factory { ...@@ -166,6 +167,8 @@ class stack_parsing_rule_factory {
return new stack_ast_filter_990_no_fixing_spaces(); return new stack_ast_filter_990_no_fixing_spaces();
case '991_no_fixing_stars': case '991_no_fixing_stars':
return new stack_ast_filter_991_no_fixing_stars(); return new stack_ast_filter_991_no_fixing_stars();
case '995_ev_modification':
return new stack_ast_filter_995_ev_modification();
case '996_call_modification': case '996_call_modification':
return new stack_ast_filter_996_call_modification(); return new stack_ast_filter_996_call_modification();
case '997_string_security': case '997_string_security':
...@@ -211,7 +214,7 @@ class stack_parsing_rule_factory { ...@@ -211,7 +214,7 @@ class stack_parsing_rule_factory {
'801_singleton_numeric', '802_singleton_units', '801_singleton_numeric', '802_singleton_units',
'910_inert_float_for_display', '910_inert_float_for_display',
'990_no_fixing_spaces', '991_no_fixing_stars', '990_no_fixing_spaces', '991_no_fixing_stars',
'996_call_modification', '995_ev_modification', '996_call_modification',
'997_string_security', '997_string_security',
'998_security', '999_strict') as $name) { '998_security', '999_strict') as $name) {
self::$singletons[$name] = self::build_from_name($name); self::$singletons[$name] = self::build_from_name($name);
......
<?php
// This file is part of Stack - http://stack.maths.ed.ac.uk/
//
// Stack 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.
//
// Stack 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 Stack. If not, see <http://www.gnu.org/licenses/>.
namespace qtype_stack;
use qtype_stack_testcase;
use stack_ast_container;
use stack_cas_keyval;
use stack_cas_security;
use stack_cas_session2;
use castext2_evaluatable;
use stack_secure_loader;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/../locallib.php');
require_once(__DIR__ . '/fixtures/test_base.php');
require_once(__DIR__ . '/../stack/cas/cassession2.class.php');
require_once(__DIR__ . '/../stack/cas/keyval.class.php');
require_once(__DIR__ . '/../stack/cas/secure_loader.class.php');
require_once(__DIR__ . '/../stack/cas/castext2/castext2_evaluatable.class.php');
/**
* Tests confirming that what happens in raw maxima will also happen in STACK
* when STACK does its own security and other rewriting.
*
* @group qtype_stack
* @covers \stack_cas_keyval
*/
class maxima_replication_test extends qtype_stack_testcase {
/**
* All tests in this set of tests share the form of having
* a section of code which will be taken as a keyval and which
* contains a variable named `RESULT` that will end up containing
* something that will be compared to the expected form. The result
* will be outputted as `string(RESULT)`. In this case, we do that
* outputting through a CASText-evaluatable.
*/
public function check($code, $result) {
// Do a full compile of the keyval.
$keyval = new stack_cas_keyval($code, null, 123);
$keyval->get_valid();
$keyval = new stack_secure_loader($keyval->compile('test')['statement']);
$output = castext2_evaluatable::make_from_source('{#RESULT#}', 'test');
$output->get_valid();
$session = new stack_cas_session2([$keyval, $output], null, 123);
// Execute.
$session->instantiate();
$this->assertEquals($result, $output->get_rendered());
}
public function test_matrix_mult() {
$code = 'simp:true;';
$code .= 'a:matrix([1,2],[3,4]);';
$code .= 'RESULT:a.a;';
$result = 'matrix([7,10],[15,22])';
$this->check($code, $result);
}
public function test_ev_flag_1() {
$code = 'simp:true;';
$code .= 'a:1+t;';
$code .= 'RESULT:a,t=1;';
$result = '2';
$this->check($code, $result);
}
public function test_ev_flag_2() {
$code = 'simp:true;';
$code .= 'a:sqrt(t);';
$code .= 'RESULT:a,t=4;';
$result = '2';
$this->check($code, $result);
}
public function test_ev_flag_3() {
$code = 'simp:false;';
$code .= 'a:1+1;';
$code .= 'RESULT:a;';
$result = '1+1';
$this->check($code, $result);
$code = 'simp:false;';
$code .= 'a:1+1,simp;';
$code .= 'RESULT:a;';
$result = '2';
$this->check($code, $result);
}
public function test_ev_flag_4() {
$code = 'simp:true;';
$code .= 'foo(x):=0+sqrt(x);';
$code .= 'RESULT:foo(t),t=4;';
$result = '2';
$this->check($code, $result);
}
public function test_ev_1() {
$code = 'simp:true;';
$code .= 'a:1+t;';
$code .= 'RESULT:ev(a,t=1);';
$result = '2';
$this->check($code, $result);
}
public function test_ev_2() {
$code = 'simp:true;';
$code .= 'a:sqrt(t);';
$code .= 'RESULT:ev(a,t=4);';
$result = '2';
$this->check($code, $result);
}
public function test_ev_3() {
$code = 'simp:false;';
$code .= 'a:ev(1+1);';
$code .= 'RESULT:a;';
$result = '1+1';
$this->check($code, $result);
$code = 'simp:false;';
$code .= 'a:ev(1+1,simp);';
$code .= 'RESULT:a;';
$result = '2';
$this->check($code, $result);
}
public function test_ev_4() {
$code = 'simp:true;';
$code .= 'foo(x):=0+sqrt(x);';
$code .= 'RESULT:ev(foo(t),t=4);';
$result = '2';
$this->check($code, $result);
}
public function test_iss824() {
$code = 'simp:true;';
$code .= 'a:2;';
$code .= 'u:matrix([t,-1,a*t]);';
$code .= 'v:matrix([-1,a+1,5]);';
$code .= 'myvar1:ev(t,solve(u.u=1,t));';
$code .= 'RESULT:ev(sqrt(u.v), t=myvar1);';
$result = 'sqrt(3)*%i';
$this->check($code, $result);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment