shithub: pprolog

ref: 13efe91101a11f41caf6321a8b2fbdd96ef9927a
dir: pprolog/prettyprint.c

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

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

Rune *prettyprintlist(Term *, Rune *, int, int, int, int, Module *);
Rune *printlist(Term *, int, int, int, Module *);
int islist(Term *);
int needsquotes(Rune *);

Rune *
prettyprint(Term *t, int quoted, int ignoreops, int numbervars, Module *mod)
{
	Rune *result;
	Rune *args;
	if(mod == nil)
		mod = getmodule(L"user");

	switch(t->tag){
	case CompoundTerm:
		if(numbervars && t->arity == 1 
		    && t->children->tag == IntegerTerm 
		    && t->children->ival >= 0 
		    && runestrcmp(t->text, L"$VAR") == 0){
			vlong n = t->children->ival;
			Rune i = L'A' + (n % 26);
			vlong j = n / 26;
			if(j == 0)
				result = runesmprint("%C", i);
			else
				result = runesmprint("%C%lld", i, j);
			break;
		}
		args = printlist(t, quoted, ignoreops, numbervars, mod);
		if(args && !ignoreops){
			result = runesmprint("[%S]", args);
			free(args);
			break;
		}
		Operator *op = getoperator(t->text, mod);
		if(op == nil || ignoreops || !(t->arity == 1 || t->arity == 2)){
			Rune *functor = prettyprint(mkatom(t->text), quoted, ignoreops, numbervars, mod);
			args = prettyprintlist(t->children, L", ", 0, quoted, ignoreops, numbervars, mod);
			result = runesmprint("%S(%S)", functor, args);
			free(functor);
			free(args);
			break;
		}else{
			/* TODO:
				1) Only print spacing between op and args when needed
				2) currectly add () around args in special cases (see 7.10.5.h.2 in spec)
			*/
			Rune *functor = prettyprint(mkatom(t->text), quoted, ignoreops, numbervars, mod);
			Rune *arg1 = prettyprint(t->children, quoted, ignoreops, numbervars, mod);
			Rune *arg2 = t->arity == 2 ? prettyprint(t->children->next, quoted, ignoreops, numbervars, mod) : nil;
			if(t->arity == 2)
				result = runesmprint("%S %S %S", arg1, functor, arg2);
			else{
				if(op->type == Xf || op->type == Yf)
					result = runesmprint("%S %S", arg1, functor);
				else
					result = runesmprint("%S %S", functor, arg1);
			}
			free(functor);
			free(arg1);
			free(arg2);
		}
		break;
	case AtomTerm:
		if(quoted && needsquotes(t->text))
			result = runesmprint("'%S'", t->text);
		else
			result = runesmprint("%S", t->text);
		break;
	case VariableTerm:
		result = runesmprint("_%ulld", t->varnr);
		break;
	case FloatTerm:
		result = runesmprint("%f", t->dval);
		break;
	case IntegerTerm:
		result = runesmprint("%lld", t->ival);
		break;
	default:
		result = runesmprint("cant print term with tag %d", t->tag);
		break;
	}

	return result;
}

Rune *
prettyprintlist(Term *t, Rune *sep, int end, int quoted, int ignoreops, int numbervars, Module *mod)
{
	if(t == nil){
		if(end)
			return runesmprint("%S", sep);
		else
			return runesmprint("");
	}

	Rune *str = prettyprint(t, quoted, ignoreops, numbervars, mod);
	Rune *rest = prettyprintlist(t->next, sep, end, quoted, ignoreops, numbervars, mod);
	Rune *result;

	if(t->next != nil)
		result = runesmprint("%S%S%S", str, sep, rest);
	else
		result = runesmprint("%S%S", str, end ? rest : L"");

	free(str);
	free(rest);
	return result;
}

/* printlist prints a list's elements but not the surrounding [ and ] */
Rune *
printlist(Term *list, int quoted, int ignoreops, int numbervars, Module *mod)
{
	if(list->tag != CompoundTerm || list->arity != 2 || runestrcmp(L".", list->text) != 0)
		return nil;

	Term *head = list->children;
	Term *tail = head->next;

	Rune *headstr = prettyprint(head, quoted, ignoreops, numbervars, mod);
	Rune *tailstr = nil;
	Rune *result;

	if(tail->tag == CompoundTerm && tail->arity == 2 && runestrcmp(L".", tail->text) == 0){
		tailstr = printlist(tail, quoted, ignoreops, numbervars, mod);
		result = runesmprint("%S, %S", headstr, tailstr);
	}else if(tail->tag == AtomTerm && runestrcmp(L"[]", tail->text) == 0){
		result = runesmprint("%S", headstr);
	}else{
		tailstr = prettyprint(tail, quoted, ignoreops, numbervars, mod);
		result = runesmprint("%S | %S", headstr, tailstr);
	}
	free(headstr);
	free(tailstr);
	return result;
}

int
needsquotes(Rune *text)
{
	Rune *graphics = L"#$&*+-./:<=>?@^~\\"; /* keep in sync with lexer */
	int len = runestrlen(text);
	int i;

	if(runestrcmp(text, L"") == 0)
		return 1;

	if(runestrchr(graphics, text[0])){
		for(i = 0; i < len; i++){
			if(!runestrchr(graphics, text[i]))
				return 1;
		}
		return 0;
	}

	if(len == 1 && runestrchr(L";!,", text[0]))
		return 0;

	if(len > 0 && !islowerrune(text[0]))
		return 1;

	for(i = 0; i < len; i++)
		if(!isalpharune(text[i]))
			return 1;
	return 0;
}