shithub: pprolog

Download patch

ref: 7441a0947cada8534adf48fa41e2d4471dfdeffc
parent: e6a38aa97b9de05ae0eede568212667b68784a60
author: Peter Mikkelsen <peter@pmikkelsen.com>
date: Wed Jul 7 20:16:30 EDT 2021

Implement the full arithmetic part of the ISO spec.

--- /dev/null
+++ b/arithmetic.c
@@ -1,0 +1,628 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+#include "dat.h"
+#include "fns.h"
+
+typedef struct ArithFunc2 ArithFunc2;
+typedef struct ArithFunc1 ArithFunc1;
+struct ArithFunc2
+{
+	Term *(*intint)(vlong, vlong);
+	Term *(*floatfloat)(double, double);
+	Term *(*floatint)(double, vlong);
+	Term *(*intfloat)(vlong, double);
+};
+
+struct ArithFunc1
+{
+	Term *(*i)(vlong);
+	Term *(*f)(double);
+};
+
+static Term *addi(vlong, vlong);
+static Term *addf(double, double);
+static Term *addfi(double, vlong);
+static Term *addif(vlong, double);
+static Term *subi(vlong, vlong);
+static Term *subf(double, double);
+static Term *subfi(double, vlong);
+static Term *subif(vlong, double);
+static Term *muli(vlong, vlong);
+static Term *mulf(double, double);
+static Term *mulfi(double, vlong);
+static Term *mulif(vlong, double);
+static Term *intdivi(vlong, vlong);
+static Term *divii(vlong, vlong);
+static Term *divf(double, double);
+static Term *divfi(double, vlong);
+static Term *divif(vlong, double);
+static Term *remi(vlong, vlong);
+static Term *modi(vlong, vlong);
+static Term *poweri(vlong, vlong);
+static Term *powerf(double, double);
+static Term *powerfi(double, vlong);
+static Term *powerif(vlong, double);
+static Term *shiftlefti(vlong, vlong);
+static Term *shiftrighti(vlong, vlong);
+static Term *bitandi(vlong, vlong);
+static Term *bitori(vlong, vlong);
+static Term *negi(vlong);
+static Term *negf(double);
+static Term *absi(vlong);
+static Term *absf(double);
+static Term *signi(vlong);
+static Term *signf(double);
+static Term *intpartf(double);
+static Term *fractpartf(double);
+static Term *floati(vlong);
+static Term *floatf(double);
+static Term *floorf(double);
+static Term *truncatef(double);
+static Term *roundf(double);
+static Term *ceilingf(double);
+static Term *sini(vlong);
+static Term *sinf(double);
+static Term *cosi(vlong);
+static Term *cosf(double);
+static Term *atani(vlong);
+static Term *atanf(double);
+static Term *expi(vlong);
+static Term *expf(double);
+static Term *logi(vlong);
+static Term *logf(double);
+static Term *sqrti(vlong);
+static Term *sqrtf(double);
+static Term *bitcompli(vlong);
+
+Term *binaryeval(Rune *, Term *, Term *, int *);
+Term *unaryeval(Rune *, Term *, int *);
+
+Term *
+aritheval(Term *expr, int *waserror)
+{
+	/* Not every arithmetic operation is defined right now. */
+	*waserror = 0;
+
+	if(expr->tag == VariableTerm){
+		*waserror = 1;
+		return instantiationerror();
+	}else if(expr->tag == AtomTerm){
+		*waserror = 1;
+		return typeerror(L"number", expr);
+	}else if(expr->tag == FloatTerm || expr->tag == IntegerTerm)
+		return expr;
+	else if(expr->tag == CompoundTerm && expr->arity == 2){
+		Term *A = aritheval(expr->children, waserror);
+		if(*waserror)
+			return A;
+
+		Term *B = aritheval(expr->children->next, waserror);
+		if(*waserror)
+			return B;
+		return binaryeval(expr->text, A, B, waserror);
+	}else if(expr->tag == CompoundTerm && expr->arity == 1){
+		Term *A = aritheval(expr->children, waserror);
+		if(*waserror)
+			return A;
+		return unaryeval(expr->text, A, waserror);
+	}else{
+		*waserror = 1;
+		Term *functor;
+		Term *arity;
+		if(expr->tag == CompoundTerm){
+			functor = mkatom(expr->text);
+			arity = mkinteger(expr->arity);
+		}else{
+			functor = expr;
+			arity = mkinteger(0);
+		}
+		functor->next = arity;
+		Term *pi = mkcompound(L"/", 2, functor);
+		return typeerror(L"evaluable", pi);
+	}	
+}
+
+Term *
+binaryeval(Rune *f, Term *a, Term *b, int *waserror)
+{
+	Term *result;
+	ArithFunc2 func;
+
+	if(runestrcmp(f, L"+") == 0)
+		func = (ArithFunc2){addi, addf, addfi, addif};
+	else if(runestrcmp(f, L"-") == 0)
+		func = (ArithFunc2){subi, subf, subfi, subif};
+	else if(runestrcmp(f, L"*") == 0)
+		func = (ArithFunc2){muli, mulf, mulfi, mulif};
+	else if(runestrcmp(f, L"//") == 0)
+		func = (ArithFunc2){intdivi, nil, nil, nil};
+	else if(runestrcmp(f, L"/") == 0)
+		func = (ArithFunc2){divii, divf, divfi, divif};
+	else if(runestrcmp(f, L"rem") == 0)
+		func = (ArithFunc2){remi, nil, nil, nil};
+	else if(runestrcmp(f, L"mod") == 0)
+		func = (ArithFunc2){modi, nil, nil, nil};
+	else if(runestrcmp(f, L"**") == 0)
+		func = (ArithFunc2){poweri, powerf, powerfi, powerif};
+	else if(runestrcmp(f, L"<<") == 0)
+		func = (ArithFunc2){shiftlefti, nil, nil, nil};
+	else if(runestrcmp(f, L">>") == 0)
+		func = (ArithFunc2){shiftrighti, nil, nil, nil};
+	else if(runestrcmp(f, L"/\\") == 0)
+		func = (ArithFunc2){bitandi, nil, nil, nil};
+	else if(runestrcmp(f, L"\\/") == 0)
+		func = (ArithFunc2){bitori, nil, nil, nil};
+	else{
+		*waserror = 1;
+		Term *functor = mkatom(f);
+		functor->next = mkinteger(2);
+		Term *pi = mkcompound(L"/", 2, functor);
+		return typeerror(L"evaluable", pi);
+	}
+
+	if(a->tag == IntegerTerm && b->tag == IntegerTerm && func.intint)
+		result = func.intint(a->ival, b->ival);
+	else if(a->tag == FloatTerm && b->tag == FloatTerm && func.floatfloat)
+		result = func.floatfloat(a->dval, b->dval);
+	else if(a->tag == FloatTerm && b->tag == IntegerTerm && func.floatint)
+		result = func.floatint(a->dval, b->ival);
+	else if(a->tag == IntegerTerm && b->tag == FloatTerm && func.intfloat)
+		result = func.intfloat(a->ival, b->dval);
+	else{
+		/* There must have been a type error */
+		int type1, type2;
+		if(func.intint){
+			type1 = IntegerTerm;
+			type2 = IntegerTerm;
+		}else if(func.floatfloat){
+			type1 = FloatTerm;
+			type2 = FloatTerm;
+		}else if(func.floatint){
+			type1 = FloatTerm;
+			type2 = IntegerTerm;
+		}else{
+			type1 = IntegerTerm;
+			type2 = FloatTerm;
+		}
+
+		if(a->tag != type1)
+			result = typeerror(type1 == IntegerTerm ? L"integer" : L"float", a);
+		else
+			result = typeerror(type2 == IntegerTerm ? L"integer" : L"float", b);
+	}
+
+	if(result->tag != IntegerTerm && result->tag != FloatTerm)
+		*waserror = 1;
+
+	return result;
+}
+
+Term *
+unaryeval(Rune *f, Term *a, int *waserror)
+{
+	Term *result;
+	ArithFunc1 func;
+
+	if(runestrcmp(f, L"-") == 0)
+		func = (ArithFunc1){negi, negf};
+	else if(runestrcmp(f, L"abs") == 0)
+		func = (ArithFunc1){absi, absf};
+	else if(runestrcmp(f, L"sign") == 0)
+		func = (ArithFunc1){signi, signf};
+	else if(runestrcmp(f, L"float_integer_part") == 0)
+		func = (ArithFunc1){nil, intpartf};
+	else if(runestrcmp(f, L"float_fractional_part") == 0)
+		func = (ArithFunc1){nil, fractpartf};
+	else if(runestrcmp(f, L"float") == 0)
+		func = (ArithFunc1){floati, floatf};
+	else if(runestrcmp(f, L"floor") == 0)
+		func = (ArithFunc1){nil, floorf};
+	else if(runestrcmp(f, L"truncate") == 0)
+		func = (ArithFunc1){nil, truncatef};
+	else if(runestrcmp(f, L"round") == 0)
+		func = (ArithFunc1){nil, roundf};
+	else if(runestrcmp(f, L"ceiling") == 0)
+		func = (ArithFunc1){nil, ceilingf};
+	else if(runestrcmp(f, L"sin") == 0)
+		func = (ArithFunc1){sini, sinf};
+	else if(runestrcmp(f, L"cos") == 0)
+		func = (ArithFunc1){cosi, cosf};
+	else if(runestrcmp(f, L"atan") == 0)
+		func = (ArithFunc1){atani, atanf};
+	else if(runestrcmp(f, L"exp") == 0)
+		func = (ArithFunc1){expi, expf};
+	else if(runestrcmp(f, L"log") == 0)
+		func = (ArithFunc1){logi, logf};
+	else if(runestrcmp(f, L"sqrt") == 0)
+		func = (ArithFunc1){sqrti, sqrtf};
+	else if(runestrcmp(f, L"\\") == 0)
+		func = (ArithFunc1){bitcompli, nil};
+	else{
+		*waserror = 1;
+		Term *functor = mkatom(f);
+		functor->next = mkinteger(1);
+		Term *pi = mkcompound(L"/", 2, functor);
+		return typeerror(L"evaluable", pi);
+	}
+
+	if(a->tag == IntegerTerm && func.i)
+		result = func.i(a->ival);
+	else if(a->tag == FloatTerm && func.f)
+		result = func.f(a->dval);
+	else{
+		if(func.i)
+			result = typeerror(L"integer", a);
+		else
+			result = typeerror(L"float", a);
+	}	
+
+	if(result->tag != IntegerTerm && result->tag != FloatTerm)
+		*waserror = 1;
+
+	return result;
+}
+
+static Term *
+addi(vlong x, vlong y)
+{
+	return mkinteger(x + y);
+}
+
+static Term *
+addf(double x, double y)
+{
+	return mkfloat(x + y);
+}
+
+static Term *
+addfi(double x, vlong y)
+{
+	return addf(x, y);
+}
+
+static Term *
+addif(vlong x, double y)
+{
+	return addf(x, y);
+}
+
+static Term *
+subi(vlong x, vlong y)
+{
+	return mkinteger(x - y);
+}
+
+static Term *
+subf(double x, double y)
+{
+	return addf(x, -y);
+}
+
+static Term *
+subfi(double x, vlong y)
+{
+	return subf(x, y);
+}
+
+static Term *
+subif(vlong x, double y)
+{
+	return subf(x, y);
+}
+
+static Term *
+muli(vlong x, vlong y)
+{
+	return mkinteger(x * y);
+}
+
+static Term *
+mulf(double x, double y)
+{
+	return mkfloat(x * y);
+}
+
+static Term *
+mulfi(double x, vlong y)
+{
+	return mulf(x, y);
+}
+
+static Term *
+mulif(vlong x, double y)
+{
+	return mulf(x, y);
+}
+
+static Term *
+intdivi(vlong x, vlong y)
+{
+	if(y == 0)
+		return evaluationerror(L"zero_divisor");
+	else
+		return mkinteger(x / y);
+}
+
+static Term *
+divii(vlong x, vlong y)
+{
+	return divf(x, y);
+}
+
+static Term *
+divf(double x, double y)
+{
+	if(y == 0)
+		return evaluationerror(L"zero_divisor");
+	else
+		return mkfloat(x / y);
+}
+
+static Term *
+divfi(double x, vlong y)
+{
+	return divf(x, y);
+}
+
+static Term *
+divif(vlong x, double y)
+{
+	return divf(x, y);
+}
+
+static Term *
+remi(vlong x, vlong y)
+{
+	if(y == 0)
+		return evaluationerror(L"zero_divisor");
+	else
+		return mkinteger(x - (x/y) * y);
+}
+
+static Term *
+modi(vlong x, vlong y)
+{
+	if(y == 0)
+		return evaluationerror(L"zero_divisor");
+	else
+		return mkinteger(x - (floor((double)x/(double)y) * y));
+}
+
+static Term *
+poweri(vlong x, vlong y)
+{
+	return powerf(x, y);
+}
+
+static Term *
+powerf(double x, double y)
+{
+	if(x == 0 && y == 0)
+		return mkfloat(1);
+	else if(x == 0 && y < 0)
+		return evaluationerror(L"undefined");
+	else
+		return mkfloat(pow(x, y));
+}
+
+static Term *
+powerfi(double x, vlong y)
+{
+	return powerf(x, y);
+}
+
+static Term *
+powerif(vlong x, double y)
+{
+	return powerf(x, y);
+}
+
+static Term *
+shiftlefti(vlong x, vlong y)
+{
+	return mkinteger(x << y);
+}
+
+static Term *
+shiftrighti(vlong x, vlong y)
+{
+	return mkinteger(x >> y);
+}
+
+static Term *
+bitandi(vlong x, vlong y)
+{
+	return mkinteger(x & y);
+}
+
+static Term *
+bitori(vlong x, vlong y)
+{
+	return mkinteger(x | y);
+}
+
+
+static Term *
+negi(vlong x)
+{
+	return mkinteger(-x);
+}
+
+static Term *
+negf(double x)
+{
+	return mkfloat(-x);
+}
+
+static Term *
+absi(vlong x)
+{
+	return mkinteger(x < 0 ? -x : x);
+}
+
+static Term *
+absf(double x)
+{
+	return mkfloat(x < 0 ? -x : x);
+}
+
+static Term *
+signi(vlong x)
+{
+	if(x < 0)
+		return mkinteger(-1);
+	else if(x > 0)
+		return mkinteger(1);
+	else
+		return mkinteger(0);
+}
+
+static Term *
+signf(double x)
+{
+	if(x < 0)
+		return mkfloat(-1);
+	else if(x > 0)
+		return mkfloat(1);
+	else
+		return mkfloat(0);
+}
+
+static Term *
+intpartf(double x)
+{
+	return mkfloat(signf(x)->dval * floorf(absf(x)->dval)->dval);
+}
+
+static Term *
+fractpartf(double x)
+{
+	return mkfloat(x - intpartf(x)->dval);
+}
+
+static Term *
+floati(vlong x)
+{
+	return mkfloat(x);
+}
+
+static Term *
+floatf(double x)
+{
+	return mkfloat(x);
+}
+
+static Term *
+floorf(double x)
+{
+	return mkfloat(floor(x));
+}
+
+static Term *
+truncatef(double x)
+{
+	if(x >= 0)
+		return floorf(x);
+	else
+		return mkfloat(-floorf(absf(x)->dval)->dval);
+}
+
+static Term *
+roundf(double x)
+{
+	return floorf(x + 0.5);
+}
+
+static Term *
+ceilingf(double x)
+{
+	return mkfloat(-floorf(-x)->dval);
+}
+
+
+static Term *
+sini(vlong x)
+{
+	return sinf(x);
+}
+
+static Term *
+sinf(double x)
+{
+	return mkfloat(sin(x));
+}
+
+static Term *
+cosi(vlong x)
+{
+	return cosf(x);
+}
+
+static Term *
+cosf(double x)
+{
+	return mkfloat(cos(x));
+}
+
+static Term *
+atani(vlong x)
+{
+	return atanf(x);
+}
+
+static Term *
+atanf(double x)
+{
+	return mkfloat(atan(x));
+}
+
+static Term *
+expi(vlong x)
+{
+	return expf(x);
+}
+
+static Term *
+expf(double x)
+{
+	return mkfloat(exp(x));
+}
+
+static Term *
+logi(vlong x)
+{
+	return logf(x);
+}
+
+static Term *
+logf(double x)
+{
+	if(x <= 0)
+		return evaluationerror(L"undefined");
+	else
+		return mkfloat(log(x));
+}
+
+static Term *
+sqrti(vlong x)
+{
+	return sqrtf(x);
+}
+
+static Term *
+sqrtf(double x)
+{
+	if(x < 0)
+		return evaluationerror(L"undefined");
+	else
+		return mkfloat(sqrt(x));
+}
+
+static Term *
+bitcompli(vlong x)
+{
+	return mkinteger(~x);
+}
+
--- a/builtins.c
+++ b/builtins.c
@@ -475,37 +475,6 @@
 	return unify(term2, t, bindings);
 }
 
-#define ToFloat(t) (t->tag == IntegerTerm ? (double)t->ival : t->dval)
-
-Term *
-aritheval(Term *expr)
-{
-	/* Not every arithmetic operation is defined right now. */
-
-	if(expr->tag == FloatTerm || expr->tag == IntegerTerm)
-		return expr;
-	else if(expr->tag == CompoundTerm && expr->arity == 2){
-		Term *A = aritheval(expr->children);
-		Term *B = aritheval(expr->children->next);
-		Term *result = mkinteger(0);
-
-		if(A == nil || B == nil)
-			return nil;
-		if(runestrcmp(expr->text, L"+") == 0){
-			if(A->tag == IntegerTerm && B->tag == IntegerTerm){
-				result->tag = IntegerTerm;
-				result->ival = A->ival + B->ival;
-			}else{
-				result->tag = FloatTerm;
-				result->dval = ToFloat(A) + ToFloat(B);
-			}
-		}else
-			return nil;
-		return result;
-	}else
-		return nil;
-}
-
 int
 builtinis(Term *goal, Binding **bindings, Module *module)
 {
@@ -513,12 +482,11 @@
 	Term *result = goal->children;
 	Term *expr = result->next;
 
-	
-	Term *realresult = aritheval(expr);
-	if(realresult)
-		return unify(result, realresult, bindings);
-	else
-		return 0;
+	int waserror;
+	Term *realresult = aritheval(expr, &waserror);
+	if(waserror)
+		Throw(realresult);
+	return unify(result, realresult, bindings);
 }
 
 int
--- a/fns.h
+++ b/fns.h
@@ -72,3 +72,6 @@
 int isnonemptylist(Term *);
 Term *listhead(Term *);
 Term *listtail(Term *);
+
+/* arithmetic.c */
+Term *aritheval(Term *, int *);
\ No newline at end of file
--- a/mkfile
+++ b/mkfile
@@ -15,6 +15,7 @@
 	streams.$O\
 	module.$O\
 	types.$O\
+	arithmetic.$O
 
 HFILES=dat.h fns.h
 
--- a/parser.c
+++ b/parser.c
@@ -536,8 +536,10 @@
 				goto Integer;
 			}
 			while(isdigitrune(peek)){
-				numD += (peek - L'0') / (double)(10 * place);
+				double addition = (peek - L'0') / (double)(10 * place);
+				numD += addition;
 				peek = Bgetrune(parsein);
+				place *= 10;
 			}
 			Bungetrune(parsein);
 			/* Should also lex 123.45E10 */