shithub: lpa

ref: 2b23d05d57743af57385cd42c0fd2d223b11d8c8
dir: /constraint.c/

View raw version
#include <u.h>
#include <libc.h>
#include <thread.h>

#include "dat.h"
#include "fns.h"

/* monadic constraints */

/* dyadic constraints */
static void constraint_equal(Ast *, Array *, Array *);


Array *
allocvar(char *name)
{
	static int id = 0;

	if(name == nil)
		name = "⎕var";

	ConstraintVar *v = alloc(DataConstraintVar);
	v->name = name;
	v->id = id++;

	Array *a = allocarray(TypeVar, 0, 1);
	setvar(a, 0, v);
	return a;
}

static Ast *
varast(Array *a)
{
	if(a == nil)
		return nil;

	if(gettype(a) == TypeVar && getrank(a) == 0){
		ConstraintVar *v = getvar(a, 0);
		if(v->ast)
			return v->ast;
	}
	Ast *c = alloc(DataAst);
	c->tag = AstConst;
	c->val = a;

	return c;
}

Array *
delayedexpr(int prim, Array *x, Array *y)
{
	Array *a = allocvar(nil);
	ConstraintVar *v = getvar(a, 0);

	Ast *func = alloc(DataAst);
	func->tag = AstPrim;
	func->prim = prim;

	Ast *e = alloc(DataAst);
	v->ast = e;
	e->func = func;
	e->tag = x ? AstDyadic : AstMonadic;
	e->left = varast(x);
	e->right = varast(y);

	return a;
}

void
graphadd(ConstraintGraph *g, Constraint *c)
{
	for(uvlong i = 0; i < g->ccount; i++){
		if(g->cs[i] == c)
			return; /* The constraint is already there. TODO: make a better test */
	}

	if(g->ccount == nelem(g->cs))
		error(EInternal, "not enough space in the constraint graph");
	g->cs[g->ccount] = c;
	g->ccount++;

	for(uvlong i = 0; i < nelem(c->vars); i++){
		ConstraintVar *v = c->vars[i];
		if(v == nil)
			continue;
		int new = 1;
		for(uvlong j = 0; j < g->vcount && new; j++){
			if(g->vs[j] == v)
				new = 0;
		}
		if(!new)
			continue;
		g->vs[g->vcount] = v;
		g->vcount++;
		for(uvlong j = 0; j < v->count; j++)
			graphadd(g, v->constraints[j]);
	}
}

Array *
solve(ConstraintVar *v)
{
	Array *res;

	if(v->ast)
		error(EDomain, "Cannot solve expression. Use ⎕assert first.");

	/* Consider the available constraints on the variable, and find a solutions (just one).
	 * If that isn't possible, fail with some appropriate error.
	 *
	 * There are of course multiple strategies to perform this search, and perhaps it would
	 * make sense if ⎕solve let the user specify one as the left argument.
	 */

	/* Build a graph containing all the variables and constraints involved.
	 * The number of max vars and constraints are fixed for now.
	 */
	ConstraintGraph *g = alloc(DataConstraintGraph);

	for(uvlong i = 0; i < v->count; i++)
		graphadd(g, v->constraints[i]);
	if(g->ccount == 0){
		/* it can have any value */
		res = allocarray(TypeNumber, 0, 1);
		setint(res, 0, 0);
	}else
		error(EInternal, "⎕solve not implemented (%ulld vars and %ulld constraints)", g->vcount, g->ccount);
	return res;
}

void
constrain(ConstraintVar *v)
{
	if(!v->ast)
		error(EDomain, "Expected a constraint expression, not a variable.");

	/* Analyse the AST and add the appropriate constraints to the variables involved.
	 * Also simplify with the constraints already there, and give an error if
	 * the simplifications show that no solutions are possible.
	 */
	int prim, dyadic;
	Array *left = nil;
	Array *right = nil;

	if(!(v->ast->tag == AstMonadic || v->ast->tag == AstDyadic))
		goto fail;
	if(v->ast->func->tag != AstPrim)
		goto fail;
	prim = v->ast->func->prim;
	dyadic = 0;
	switch(v->ast->tag){
	case AstDyadic:
		dyadic = 1;
		if(v->ast->left->tag != AstConst)
			goto fail;
		left = v->ast->left->val;
		/* fall through */
	case AstMonadic:
		if(v->ast->right->tag != AstConst)
			goto fail;
		right = v->ast->right->val;
	}

	switch(prim){
	case PMatch:
		if(dyadic)
			constraint_equal(v->ast, left, right);
		else
			goto fail;
		break;
	default:
		goto fail;
	}
	return;

fail:
	error(EInternal, "don't know how to assert the given constraint");
}

static void
applyconstraint(Constraint *c)
{
	/* Find the variables involved */
	Array *args[2];
	args[0] = c->left;
	args[1] = c->right;
	int nvars = 0;

	for(int i = 0; i < nelem(args); i++){
		Array *a = args[i];
		if(gettype(a) != TypeVar || getrank(a) != 0)
			continue;
		ConstraintVar *v = getvar(a, 0);
		c->vars[nvars] = v;
		nvars++;

		v->count++;
		v->constraints = allocextra(v, sizeof(c) * v->count);
		v->constraints[v->count-1] = c;
	}

	/* Should simplify here as well */
}

/* monadic constraints */

/* dyadic constraints */

static void
constraint_equal(Ast *a, Array *x, Array *y)
{
	Constraint *c = alloc(DataConstraint);
	c->tag = CEqual;
	c->ast = a;
	c->left = x;
	c->right = y;
	applyconstraint(c);
}