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>
This commit is contained in:
Christophe de Dinechin 2024-09-23 19:32:05 +02:00
parent bd3ed1bd05
commit 84e2b4ad21
15 changed files with 78 additions and 63 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
color-images/istack-22b.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
images/istack-22b.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View file

@ -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())
{

View file

@ -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,

View file

@ -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);

View file

@ -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;

View file

@ -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)")