explode: Extract arguments for a function call into arguments
The `Obj→` command now explodes the argments to a function call into an array. It turns a function call object into its arguments. Optimize the internal logic for grabbing arguments during expression rewrites to avoid building expressions when matching a single object. This exposed a bug in rewrite rules for `collect` and `expand`, which were reordering terms using two different methods, causing the rewrites to go in a loop. In order to use only the reordering rule based on memory ordering, while at the same time putting constants before variables (i.e. rewrite `A*2` as `2*A`), the order of memory comparison when objects have a different type is now reversed. Signed-off-by: Christophe de Dinechin <christophe@dinechin.org> Signed-off-by: Christophe de Dinechin <christophe@dinechin.org>
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 3.4 KiB |
BIN
color-images/istack-22b.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 3.3 KiB |
BIN
images/istack-22b.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.3 KiB |
|
@ -262,6 +262,13 @@ COMMAND_BODY(Explode)
|
|||
rt.push(obj);
|
||||
}
|
||||
break;
|
||||
case ID_funcall:
|
||||
if (list_p lst = funcall_p(obj)->args())
|
||||
{
|
||||
rt.top(lst);
|
||||
return OK;
|
||||
}
|
||||
break;
|
||||
case ID_array:
|
||||
if (rt.drop())
|
||||
{
|
||||
|
|
|
@ -604,7 +604,7 @@ size_t expression::required_memory(id type, id op,
|
|||
//
|
||||
// Lowercase names must be sorted, i.e. x<=y and u<v.
|
||||
|
||||
static expression_p grab_arguments(size_t &eq, size_t &eqsz)
|
||||
static object_p grab_arguments(size_t &eq, size_t &eqsz)
|
||||
// ----------------------------------------------------------------------------
|
||||
// Fetch an argument using the arity to know how many things to use
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -630,6 +630,17 @@ static expression_p grab_arguments(size_t &eq, size_t &eqsz)
|
|||
if (symbol_g *ref = expression::independent)
|
||||
if (!expression::contains_independent_variable)
|
||||
sym = *ref;
|
||||
|
||||
if (sz == 1)
|
||||
{
|
||||
object_p obj = rt.stack(eq);
|
||||
if (sym && sym->found_in(obj))
|
||||
expression::contains_independent_variable = true;
|
||||
eq += sz;
|
||||
eqsz -= sz;
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (sym)
|
||||
{
|
||||
while (len--)
|
||||
|
@ -652,9 +663,8 @@ static expression_p grab_arguments(size_t &eq, size_t &eqsz)
|
|||
}
|
||||
eq += sz;
|
||||
eqsz -= sz;
|
||||
list_p list = list::make(object::ID_expression,
|
||||
scr.scratch(), scr.growth());
|
||||
return expression_p(list);
|
||||
list_p a = list::make(object::ID_expression, scr.scratch(), scr.growth());
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2573,6 +2583,37 @@ EVAL_BODY(funcall)
|
|||
}
|
||||
|
||||
|
||||
array_p funcall::args() const
|
||||
// ----------------------------------------------------------------------------
|
||||
// Return an array with the arguments to the funcall
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
size_t depth = rt.depth();
|
||||
if (!expand_without_size())
|
||||
return nullptr;
|
||||
scribble scr;
|
||||
while (object_p obj = arg(depth))
|
||||
if (!rt.append(obj->size(), byte_p(obj)))
|
||||
return nullptr;
|
||||
return array_p(list::make(ID_array, scr.scratch(), scr.growth()));
|
||||
}
|
||||
|
||||
|
||||
object_p funcall::arg(uint depth) const
|
||||
// ----------------------------------------------------------------------------
|
||||
// Return the outermost argument found in a function call
|
||||
// ----------------------------------------------------------------------------
|
||||
{
|
||||
if (rt.depth() <= depth)
|
||||
return nullptr;
|
||||
size_t sz = rt.depth() - depth;
|
||||
size_t eq = 0;
|
||||
object_p result = grab_arguments(eq, sz);
|
||||
rt.drop(eq);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
COMMAND_BODY(Apply)
|
||||
// ----------------------------------------------------------------------------
|
||||
// Apply arguments to build a function call
|
||||
|
@ -2802,13 +2843,18 @@ bool expression::split_equation(expression_g &left, expression_g &right) const
|
|||
{
|
||||
size_t eq = 1;
|
||||
size_t len = rt.depth() - depth - eq;
|
||||
if (expression_g r = grab_arguments(eq, len))
|
||||
if (object_g r = grab_arguments(eq, len))
|
||||
{
|
||||
if (expression_g l = grab_arguments(eq, len))
|
||||
if (object_g l = grab_arguments(eq, len))
|
||||
{
|
||||
right = r;
|
||||
left = l;
|
||||
result = true;
|
||||
expression_g ra = expression::as_expression(r);
|
||||
expression_g la = expression::as_expression(l);
|
||||
if (la && ra)
|
||||
{
|
||||
right = ra;
|
||||
left = la;
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2874,10 +2920,8 @@ expression_p expression::expand() const
|
|||
// Group terms
|
||||
v + u, u + v,
|
||||
X + v + u, X + u + v,
|
||||
A + X, X + A,
|
||||
v * u, u * v,
|
||||
X * v * u, X * u * v,
|
||||
X * A, A * X,
|
||||
|
||||
// Sign change simplifications
|
||||
X + (-Y), X - Y,
|
||||
|
@ -2960,10 +3004,8 @@ expression_p expression::collect() const
|
|||
X + (-Y), X - Y,
|
||||
|
||||
// Group terms
|
||||
X * A, A * X,
|
||||
X * v * u, X * u * v,
|
||||
v * u, u * v,
|
||||
A + X, X + A,
|
||||
X + v + u, X + u + v,
|
||||
v + u, u + v,
|
||||
|
||||
|
|
|
@ -350,8 +350,8 @@ struct funcall : expression
|
|||
funcall(id type, id op, algebraic_g args[], uint arity)
|
||||
: expression(type, op, args, arity) {}
|
||||
|
||||
static grob_p graph(grapher &g, uint depth, int &precedence);
|
||||
static symbol_p render(uint depth, int &precedence, bool edit);
|
||||
object_p arg(uint depth) const;
|
||||
array_p args() const;
|
||||
|
||||
public:
|
||||
OBJECT_DECL(funcall);
|
||||
|
|
|
@ -1502,7 +1502,7 @@ int object::compare_to(object_p other) const
|
|||
id ty = type();
|
||||
id oty = other->type();
|
||||
if (ty != oty)
|
||||
return ty < oty ? -1 : 1;
|
||||
return ty > oty ? -1 : 1;
|
||||
size_t sz = size();
|
||||
size_t osz = other->size();
|
||||
size_t ssz = sz < osz ? sz : osz;
|
||||
|
|
60
src/tests.cc
|
@ -1313,6 +1313,9 @@ void tests::interactive_stack_operations()
|
|||
step("Interactive stack revert")
|
||||
.test(RSHIFT, F4)
|
||||
.image_noheader("istack-22", 0, 1000);
|
||||
step("Interactive stack DropN")
|
||||
.test(KEY2, LSHIFT, F2)
|
||||
.image_noheader("istack-22b", 0, 1000);
|
||||
step("Interactive stack value sort")
|
||||
.test(NOSHIFT, KEY3, RSHIFT, F2)
|
||||
.image_noheader("istack-23", 0, 1000);
|
||||
|
@ -1323,64 +1326,27 @@ void tests::interactive_stack_operations()
|
|||
step("Interactive stack DupN and sort")
|
||||
.test(ENTER, CLEAR, "111 222 333 444", ENTER,
|
||||
UP, KEY3, LSHIFT, F1, KEY6, RSHIFT, F2, ENTER)
|
||||
.expect("222")
|
||||
.test(BSP).expect("222")
|
||||
.test(BSP).expect("333")
|
||||
.test(BSP).expect("333")
|
||||
.test(BSP).expect("444")
|
||||
.test(BSP).expect("444")
|
||||
.test(BSP).expect("111")
|
||||
.test(BSP).noerror()
|
||||
.test(BSP).error("Too few arguments");
|
||||
.got("222", "222", "333", "333", "444", "444", "111");
|
||||
|
||||
step("Interactive stack DupN and non-reverted sort")
|
||||
.test(ENTER, CLEAR, "123 456 789 ABC", ENTER,
|
||||
UP, KEY3, LSHIFT, F1, KEY6, RSHIFT, F3, ENTER)
|
||||
.expect("'ABC'")
|
||||
.test(BSP).expect("'ABC'")
|
||||
.test(BSP).expect("789")
|
||||
.test(BSP).expect("789")
|
||||
.test(BSP).expect("456")
|
||||
.test(BSP).expect("456")
|
||||
.test(BSP).expect("123")
|
||||
.test(BSP).noerror()
|
||||
.test(BSP).error("Too few arguments");
|
||||
.got("789", "789", "456", "456", "'ABC'", "'ABC'", "123");
|
||||
|
||||
step("Interactive stack DupN and reverted sort")
|
||||
.test(ENTER, CLEAR, "123 456 789 ABC", ENTER,
|
||||
UP, KEY3, LSHIFT, F1, KEY6, RSHIFT, F3, RSHIFT, F4, ENTER)
|
||||
.expect("456")
|
||||
.test(BSP).expect("456")
|
||||
.test(BSP).expect("789")
|
||||
.test(BSP).expect("789")
|
||||
.test(BSP).expect("'ABC'")
|
||||
.test(BSP).expect("'ABC'")
|
||||
.test(BSP).expect("123")
|
||||
.test(BSP).noerror()
|
||||
.test(BSP).error("Too few arguments");
|
||||
.got("'ABC'", "'ABC'", "456", "456", "789", "789", "123");
|
||||
|
||||
step("Interactive stack Keep")
|
||||
.test(ENTER, CLEAR, "123 456 789 ABC DEF GHI", ENTER,
|
||||
UP, UP, UP, LSHIFT, F3, ENTER)
|
||||
.expect("'GHI'")
|
||||
.test(BSP).expect("'DEF'")
|
||||
.test(BSP).expect("'ABC'")
|
||||
.test(BSP).noerror()
|
||||
.test(BSP).error("Too few arguments");
|
||||
.got("'GHI'", "'DEF'", "'ABC'");
|
||||
|
||||
step("Interactive stack Swap and Level")
|
||||
.test(ENTER, CLEAR, "123 456 789 ABC DEF GHI", ENTER,
|
||||
UP, UP, UP, RSHIFT, F5, RSHIFT, F6, ENTER)
|
||||
.expect("3")
|
||||
.test(BSP).expect("'GHI'")
|
||||
.test(BSP).expect("'DEF'")
|
||||
.test(BSP).expect("789")
|
||||
.test(BSP).expect("'ABC'")
|
||||
.test(BSP).expect("456")
|
||||
.test(BSP).expect("123")
|
||||
.test(BSP).noerror()
|
||||
.test(BSP).error("Too few arguments");
|
||||
|
||||
.got("3", "'GHI'", "'DEF'", "789", "'ABC'", "456", "123");
|
||||
}
|
||||
|
||||
|
||||
|
@ -5219,19 +5185,19 @@ void tests::sorting_functions()
|
|||
|
||||
step("Value sort (SORT)")
|
||||
.test(CLEAR, "{ 7 2.5 3 9.2 \"DEF\" 8.4 \"ABC\" } SORT", ENTER)
|
||||
.expect("{ \"ABC\" \"DEF\" 2.5 3 7 8.4 9.2 }");
|
||||
.expect("{ 2.5 3 7 8.4 9.2 \"ABC\" \"DEF\" }");
|
||||
step("Reverse list (REVLIST)")
|
||||
.test("revlist", ENTER)
|
||||
.expect("{ 9.2 8.4 7 3 2.5 \"DEF\" \"ABC\" }");
|
||||
.expect("{ \"DEF\" \"ABC\" 9.2 8.4 7 3 2.5 }");
|
||||
step("Memory sort (QUICKSORT)")
|
||||
.test("QUICKSORT", ENTER)
|
||||
.expect("{ \"ABC\" \"DEF\" 3 7 2.5 8.4 9.2 }");
|
||||
.expect("{ 2.5 8.4 9.2 3 7 \"ABC\" \"DEF\" }");
|
||||
step("Reverse memory sort (ReverseQuickSort)")
|
||||
.test("reverseQuickSort", ENTER)
|
||||
.expect("{ 9.2 8.4 2.5 7 3 \"DEF\" \"ABC\" }");
|
||||
.expect("{ \"DEF\" \"ABC\" 7 3 9.2 8.4 2.5 }");
|
||||
step("Reverse sort (ReverseSort)")
|
||||
.test("ReverseSort", ENTER)
|
||||
.expect("{ 9.2 8.4 7 3 2.5 \"DEF\" \"ABC\" }");
|
||||
.expect("{ \"DEF\" \"ABC\" 9.2 8.4 7 3 2.5 }");
|
||||
step("Min function (integer)")
|
||||
.test(CLEAR, "1 2 MIN", ENTER).expect("1");
|
||||
step("Max function (integer)")
|
||||
|
|