ref: d81447526cde6fa98dfa792a65f71acb78ef1398
dir: /builtins.c/
#include <u.h> #include <libc.h> #include "dat.h" #include "fns.h" #define BuiltinProto(name) int name(Term *, Term *, Goal **, Choicepoint **, Binding **) #define Match(X, Y) (runestrcmp(name, X) == 0 && arity == Y) #define Throw(What) do{\ Goal *g = malloc(sizeof(Goal)); \ g->goal = What; \ g->catcher = nil; \ g->next = *goals; \ *goals = g; \ return 1; \ }while(0) BuiltinProto(builtinfail); BuiltinProto(builtincall); BuiltinProto(builtincut); BuiltinProto(builtinvar); BuiltinProto(builtinatom); BuiltinProto(builtininteger); BuiltinProto(builtinfloat); BuiltinProto(builtinatomic); BuiltinProto(builtincompound); BuiltinProto(builtinnonvar); BuiltinProto(builtinnumber); BuiltinProto(builtincompare); BuiltinProto(builtinfunctor); BuiltinProto(builtinarg); BuiltinProto(builtinuniv); BuiltinProto(builtinis); BuiltinProto(builtincatch); BuiltinProto(builtinthrow); BuiltinProto(builtinsetprologflag); BuiltinProto(builtincurrentprologflag); BuiltinProto(builtinopen); BuiltinProto(builtinclose); BuiltinProto(builtincurrentinput); BuiltinProto(builtincurrentoutput); BuiltinProto(builtinsetinput); BuiltinProto(builtinsetoutput); int compareterms(Term *, Term *); Builtin findbuiltin(Term *goal) { int arity; Rune *name; switch(goal->tag){ case AtomTerm: arity = 0; name = goal->text; break; case CompoundTerm: arity = goal->arity; name = goal->text; break; default: return nil; } /* Rewrite this so its not just a long if chain */ if(Match(L"fail", 0)) return builtinfail; if(Match(L"call", 1)) return builtincall; if(Match(L"!", 0)) return builtincut; if(Match(L"var", 1)) return builtinvar; if(Match(L"atom", 1)) return builtinatom; if(Match(L"integer", 1)) return builtininteger; if(Match(L"float", 1)) return builtinfloat; if(Match(L"atomic", 1)) return builtinatomic; if(Match(L"compound", 1)) return builtincompound; if(Match(L"nonvar", 1)) return builtinnonvar; if(Match(L"number", 1)) return builtinnumber; if(Match(L"compare", 3)) return builtincompare; if(Match(L"functor", 3)) return builtinfunctor; if(Match(L"arg", 3)) return builtinarg; if(Match(L"=..", 2)) return builtinuniv; if(Match(L"is", 2)) return builtinis; if(Match(L"catch", 3)) return builtincatch; if(Match(L"throw", 1)) return builtinthrow; if(Match(L"set_prolog_flag", 2)) return builtinsetprologflag; if(Match(L"current_prolog_flag", 2)) return builtincurrentprologflag; if(Match(L"open", 4)) return builtinopen; if(Match(L"close", 2)) return builtinclose; if(Match(L"current_input", 1)) return builtincurrentinput; if(Match(L"current_output", 1)) return builtincurrentoutput; if(Match(L"set_input", 1)) return builtinsetinput; if(Match(L"set_output", 1)) return builtinsetoutput; return nil; } int builtinfail(Term *database, Term *goal, Goal **goals, Choicepoint **choicestack, Binding **bindings) { USED(database); USED(goal); USED(goals); USED(choicestack); USED(bindings); return 0; } int builtincall(Term *database, Term *goal, Goal **goals, Choicepoint **choicestack, Binding **bindings) { USED(database); USED(choicestack); USED(bindings); Goal *g = malloc(sizeof(Goal)); g->goal = goal->children; g->catcher = nil; g->next = *goals; *goals = g; return 1; } int builtincut(Term *database, Term *goal, Goal **goals, Choicepoint **choicestack, Binding **bindings) { USED(database); USED(goals); USED(bindings); Choicepoint *cp = *choicestack; /* Cut all choicepoints with an id larger or equal to the goal clause number, since they must have been introduced after this goal's parent. */ while(cp != nil && cp->id >= goal->clausenr) cp = cp->next; *choicestack = cp; return 1; } int builtinvar(Term *database, Term *goal, Goal **goals, Choicepoint **choicestack, Binding **bindings) { USED(database); USED(goals); USED(choicestack); USED(bindings); Term *arg = goal->children; return (arg->tag == VariableTerm); } int builtinatom(Term *database, Term *goal, Goal **goals, Choicepoint **choicestack, Binding **bindings) { USED(database); USED(goals); USED(choicestack); USED(bindings); Term *arg = goal->children; return (arg->tag == AtomTerm); } int builtininteger(Term *database, Term *goal, Goal **goals, Choicepoint **choicestack, Binding **bindings) { USED(database); USED(goals); USED(choicestack); USED(bindings); Term *arg = goal->children; return (arg->tag == NumberTerm && arg->numbertype == NumberInt); } int builtinfloat(Term *database, Term *goal, Goal **goals, Choicepoint **choicestack, Binding **bindings) { USED(database); USED(goals); USED(choicestack); USED(bindings); Term *arg = goal->children; return (arg->tag == NumberTerm && arg->numbertype == NumberFloat); } int builtinatomic(Term *database, Term *goal, Goal **goals, Choicepoint **choicestack, Binding **bindings) { USED(database); USED(goals); USED(choicestack); USED(bindings); Term *arg = goal->children; return (arg->tag == AtomTerm || arg->tag == NumberTerm); } int builtincompound(Term *database, Term *goal, Goal **goals, Choicepoint **choicestack, Binding **bindings) { USED(database); USED(goals); USED(choicestack); USED(bindings); Term *arg = goal->children; return (arg->tag == CompoundTerm); } int builtinnonvar(Term *database, Term *goal, Goal **goals, Choicepoint **choicestack, Binding **bindings) { USED(database); USED(goals); USED(choicestack); USED(bindings); Term *arg = goal->children; return (arg->tag != VariableTerm); } int builtinnumber(Term *database, Term *goal, Goal **goals, Choicepoint **choicestack, Binding **bindings) { USED(database); USED(goals); USED(choicestack); USED(bindings); Term *arg = goal->children; return (arg->tag == NumberTerm); } #define Compare(A, B) ((A < B) ? -1 : ((A > B) ? 1 : 0)) int compareterms(Term *t1, Term *t2) { int result = 0; if(t1->tag != t2->tag) result = Compare(t1->tag, t2->tag); else{ /* Same type term */ switch(t1->tag){ case VariableTerm: if(t1->clausenr == t2->clausenr) result = runestrcmp(t1->text, t2->text); else result = Compare(t1->clausenr, t2->clausenr); break; case NumberTerm: if(t1->numbertype == t2->numbertype){ if(t1->numbertype == NumberInt) result = Compare(t1->ival, t2->ival); else result = Compare(t1->dval, t2->dval); }else result = Compare(t1->numbertype, t2->numbertype); break; case AtomTerm: result = runestrcmp(t1->text, t2->text); break; case CompoundTerm: result = Compare(t1->arity, t2->arity); if(result != 0) break; result = runestrcmp(t1->text, t2->text); if(result != 0) break; t1 = t1->children; t2 = t2->children; while(t1 != nil && t2 != nil){ result = compareterms(t1, t2); if(result != 0) break; else t1 = t1->next; t2 = t2->next; } break; } } return result; } int builtincompare(Term *database, Term *goal, Goal **goals, Choicepoint **choicestack, Binding **bindings) { USED(database); USED(goals); USED(choicestack); Term *order = goal->children; Term *t1 = order->next; Term *t2 = t1->next; int result = compareterms(t1, t2); Term *resultorder; if(result == -1) resultorder = mkatom(L"<"); else if(result == 0) resultorder = mkatom(L"="); else resultorder = mkatom(L">"); return unify(order, resultorder, bindings); } int builtinfunctor(Term *database, Term *goal, Goal **goals, Choicepoint **choicestack, Binding **bindings) { USED(database); USED(goals); USED(choicestack); Term *term = goal->children; Term *name = term->next; Term *arity = name->next; if(term->tag == CompoundTerm){ Term *realname = mkatom(term->text); Term *realarity = mknumber(NumberInt, term->arity, 0); if(unify(name, realname, bindings) && unify(arity, realarity, bindings)) return 1; }else if(arity->tag == NumberTerm && arity->numbertype == NumberInt && (name->tag == AtomTerm || name->tag == NumberTerm)){ if(arity->ival == 0) return unify(term, name, bindings); else{ if(name->tag != AtomTerm) return 0; /* Make arity maky fresh variables */ int i; Term *args = nil; for(i = 0; i < arity->ival; i++){ Rune *varname = runesmprint("FunctorVar%d", i); Term *arg = mkvariable(varname); args = appendterm(args, arg); } Term *realterm = mkcompound(name->text, arity->ival, args); return unify(term, realterm, bindings); } } return 0; } int builtinarg(Term *database, Term *goal, Goal **goals, Choicepoint **choicestack, Binding **bindings) { USED(database); USED(goals); USED(choicestack); Term *n = goal->children; Term *term = n->next; Term *arg = term->next; if(n->tag != NumberTerm || n->numbertype != NumberInt) return 0; if(n->ival < 0) Throw(domainerror(L"not_less_than_zero", n)); if(term->tag != CompoundTerm) return 0; if(n->ival >= term->arity) return 0; int i; Term *t; for(i = 0, t = term->children; i < n->ival; i++, t = t->next); return unify(arg, t, bindings); } int listlength(Term *term) { if(term->tag == AtomTerm && runestrcmp(term->text, L"[]") == 0) return 0; else if(term->tag == CompoundTerm && term->arity == 2 && runestrcmp(term->text, L".") == 0){ int taillength = listlength(term->children->next); return (taillength == -1) ? -1 : 1 + taillength; }else return -1; } int builtinuniv(Term *database, Term *goal, Goal **goals, Choicepoint **choicestack, Binding **bindings) { USED(database); USED(goals); USED(choicestack); Term *term = goal->children; Term *list = term->next; int len; if(term->tag == VariableTerm){ Rune *name; Term *elems = nil; Term *realterm; int i; len = listlength(list); if(len < 1) return 0; if(list->children->tag != AtomTerm) return 0; name = list->children->text; list = list->children->next; for(i = 1; i < len; i++){ Term *t = copyterm(list->children, nil); elems = appendterm(elems, t); list = list->children->next; } realterm = mkcompound(name, len-1, elems); return unify(term, realterm, bindings); }else if(term->tag == CompoundTerm){ Term *elems = mkatom(term->text); elems->next = term->children; Term *reallist = mklist(elems); return unify(list, reallist, bindings); }else{ Term *t = copyterm(term, nil); t->next = mkatom(L"[]"); Term *reallist = mkcompound(L".", 2, t); return unify(list, reallist, bindings); } } #define ToFloat(t) (t->numbertype == NumberInt ? (double)t->ival : t->dval) Term * aritheval(Term *expr) { /* Not every arithmetic operation is defined right now. */ if(expr->tag == NumberTerm) return expr; else if(expr->tag == CompoundTerm && expr->arity == 2){ Term *A = aritheval(expr->children); Term *B = aritheval(expr->children->next); Term *result = mknumber(NumberInt, 0, 0); if(A == nil || B == nil) return nil; if(runestrcmp(expr->text, L"+") == 0){ if(A->numbertype == NumberInt && B->numbertype == NumberInt){ result->numbertype = NumberInt; result->ival = A->ival + B->ival; }else{ result->numbertype = NumberFloat; result->dval = ToFloat(A) + ToFloat(B); } }else return nil; return result; }else return nil; } int builtinis(Term *database, Term *goal, Goal **goals, Choicepoint **choicestack, Binding **bindings) { USED(database); USED(goals); USED(choicestack); Term *result = goal->children; Term *expr = result->next; Term *realresult = aritheval(expr); if(realresult) return unify(result, realresult, bindings); else return 0; } int builtincatch(Term *database, Term *goal, Goal **goals, Choicepoint **choicestack, Binding **bindings) { USED(database); USED(choicestack); USED(bindings); Term *catchgoal = goal->children; Term *catcher = catchgoal->next; Term *recover = catcher->next; Goal *catchframe = malloc(sizeof(Goal)); catchframe->goal = recover; catchframe->catcher = catcher; catchframe->next = *goals; *goals = catchframe; Goal *g = malloc(sizeof(Goal)); g->goal = catchgoal; g->catcher = nil; g->next = *goals; *goals = g; return 1; } int builtinthrow(Term *database, Term *goal, Goal **goals, Choicepoint **choicestack, Binding **bindings) { USED(database); USED(choicestack); USED(bindings); USED(goals); Term *ball = goal->children; print("Throwing: %S\n", prettyprint(ball)); Goal *g; for(g = *goals; g != nil; g = g->next){ if(g->catcher == nil) continue; if(unify(g->catcher, ball, bindings)){ if(g->goal == nil){ /* As soon as we have print facilities as builtins, we can avoid this by having the protector frame have a unhandled exception handler*/ print("Unhandled exception: %S\n", prettyprint(ball)); exits("exception"); return 0; }else{ *goals = g->next; Goal *newgoal = malloc(sizeof(Goal)); newgoal->goal = copyterm(g->goal, nil); newgoal->catcher = nil; newgoal->next = *goals; *goals = newgoal; applybinding(newgoal->goal, *bindings); Choicepoint *cp = *choicestack; while(cp != nil && cp->id >= goal->clausenr) cp = cp->next; *choicestack = cp; return 1; } } } return 0; } int builtincurrentprologflag(Term *database, Term *goal, Goal **goals, Choicepoint **choicestack, Binding **bindings) { USED(database); USED(goal); USED(goals); USED(choicestack); USED(bindings); return 0; } int builtinsetprologflag(Term *database, Term *goal, Goal **goals, Choicepoint **choicestack, Binding **bindings) { USED(database); USED(choicestack); USED(bindings); Term *key = goal->children; Term *value = key->next; if(key->tag == VariableTerm || value->tag == VariableTerm) Throw(instantiationerror()); if(key->tag != AtomTerm) Throw(typeerror(L"atom", key)); Term *error = setflag(key->text, value); if(error) Throw(error); return 1; } int builtinopen(Term *database, Term *goal, Goal **goals, Choicepoint **choicestack, Binding **bindings) { USED(database); USED(goals); USED(choicestack); USED(bindings); Term *sourcesink = goal->children; Term *mode = sourcesink->next; Term *stream = mode->next; Term *options = stream->next; if(sourcesink->tag == VariableTerm || mode->tag == VariableTerm || options->tag == VariableTerm) Throw(instantiationerror()); if(stream->tag != VariableTerm) Throw(typeerror(L"variable", stream)); if(options->tag != AtomTerm || runestrcmp(options->text, L"[]") != 0) Throw(typeerror(L"empty_list", options)); if(mode->tag != AtomTerm) Throw(typeerror(L"atom", mode)); if(sourcesink->tag != AtomTerm) Throw(domainerror(L"source_sink", sourcesink)); Term *newstream; int error = openstream(sourcesink->text, mode->text, options, &newstream); if(error) Throw(newstream); else return unify(stream, newstream, bindings); return 0; } int builtinclose(Term *database, Term *goal, Goal **goals, Choicepoint **choicestack, Binding **bindings) { USED(database); USED(goals); USED(choicestack); USED(bindings); Term *stream = goal->children; Term *options = stream->next; if(stream->tag == VariableTerm || options->tag == VariableTerm) Throw(instantiationerror()); if(options->tag != AtomTerm || runestrcmp(options->text, L"[]") != 0) Throw(typeerror(L"empty_list", options)); if((stream->tag != NumberTerm || stream->numbertype != NumberInt) && stream->tag != AtomTerm) Throw(domainerror(L"stream_or_alias", stream)); if(!isopenstream(stream)) Throw(existenceerror(L"stream", stream)); closestream(stream); return 1; } int builtincurrentinput(Term *database, Term *goal, Goal **goals, Choicepoint **choicestack, Binding **bindings) { USED(database); USED(goals); USED(choicestack); USED(bindings); Term *stream = goal->children; if(stream->tag != VariableTerm && (stream->tag != NumberTerm || stream->numbertype != NumberInt)) Throw(domainerror(L"stream", stream)); Term *current = currentinputstream(); return unify(stream, current, bindings); } int builtincurrentoutput(Term *database, Term *goal, Goal **goals, Choicepoint **choicestack, Binding **bindings) { USED(database); USED(goals); USED(choicestack); USED(bindings); Term *stream = goal->children; if(stream->tag != VariableTerm && (stream->tag != NumberTerm || stream->numbertype != NumberInt)) Throw(domainerror(L"stream", stream)); Term *current = currentoutputstream(); return unify(stream, current, bindings); } int builtinsetinput(Term *database, Term *goal, Goal **goals, Choicepoint **choicestack, Binding **bindings) { USED(database); USED(goals); USED(choicestack); USED(bindings); Term *stream = goal->children; if(stream->tag == VariableTerm) Throw(instantiationerror()); if((stream->tag != NumberTerm || stream->numbertype != NumberInt) && stream->tag != AtomTerm) Throw(domainerror(L"stream_or_alias", stream)); if(!isopenstream(stream)) Throw(existenceerror(L"stream", stream)); if(!isinputstream(stream)) Throw(permissionerror(L"input", L"stream", stream)); setcurrentinputstream(stream); return 1; } int builtinsetoutput(Term *database, Term *goal, Goal **goals, Choicepoint **choicestack, Binding **bindings) { USED(database); USED(goal); USED(goals); USED(choicestack); USED(bindings); Term *stream = goal->children; if(stream->tag == VariableTerm) Throw(instantiationerror()); if((stream->tag != NumberTerm || stream->numbertype != NumberInt) && stream->tag != AtomTerm) Throw(domainerror(L"stream_or_alias", stream)); if(!isopenstream(stream)) Throw(existenceerror(L"stream", stream)); if(!isoutputstream(stream)) Throw(permissionerror(L"output", L"stream", stream)); setcurrentoutputstream(stream); return 1; }