ref: 0f958749e189e4dacd7a1f70cfc33460e1228d3b
dir: /builtins.c/
#include <u.h> #include <libc.h> #include <bio.h> #include "dat.h" #include "fns.h" #define BuiltinProto(name) int name(Term *, Binding **, Module *) #define Match(X, Y) (runestrcmp(name, X) == 0 && arity == Y) #define Throw(What) do{\ Goal *g = malloc(sizeof(Goal)); \ g->goal = What; \ g->module = usermodule; \ g->catcher = nil; \ g->next = goalstack; \ goalstack = 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); BuiltinProto(builtinreadterm); BuiltinProto(builtinwriteterm); 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; if(Match(L"read_term", 3)) return builtinreadterm; if(Match(L"write_term", 3)) return builtinwriteterm; return nil; } int builtinfail(Term *goal, Binding **bindings, Module *module) { USED(goal); USED(bindings); USED(module); return 0; } int builtincall(Term *goal, Binding **bindings, Module *module) { USED(bindings); goalstack = addgoals(goalstack, goal->children, module); return 1; } int builtincut(Term *goal, Binding **bindings, Module *module) { USED(bindings); USED(module); 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 *goal, Binding **bindings, Module *module) { USED(bindings); USED(module); Term *arg = goal->children; return (arg->tag == VariableTerm); } int builtinatom(Term *goal, Binding **bindings, Module *module) { USED(bindings); USED(module); Term *arg = goal->children; return (arg->tag == AtomTerm); } int builtininteger(Term *goal, Binding **bindings, Module *module) { USED(bindings); USED(module); Term *arg = goal->children; return (arg->tag == IntegerTerm); } int builtinfloat(Term *goal, Binding **bindings, Module *module) { USED(bindings); USED(module); Term *arg = goal->children; return (arg->tag == FloatTerm); } int builtinatomic(Term *goal, Binding **bindings, Module *module) { USED(bindings); USED(module); Term *arg = goal->children; return (arg->tag == AtomTerm || arg->tag == FloatTerm || arg->tag == IntegerTerm); } int builtincompound(Term *goal, Binding **bindings, Module *module) { USED(bindings); USED(module); Term *arg = goal->children; return (arg->tag == CompoundTerm); } int builtinnonvar(Term *goal, Binding **bindings, Module *module) { USED(bindings); USED(module); Term *arg = goal->children; return (arg->tag != VariableTerm); } int builtinnumber(Term *goal, Binding **bindings, Module *module) { USED(bindings); USED(module); Term *arg = goal->children; return (arg->tag == FloatTerm || arg->tag == IntegerTerm); } #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(runestrcmp(t1->text, L"_") == 0 && runestrcmp(t2->text, L"_") == 0) result = 1; /* Special case since _ and _ are always different */ else if(t1->clausenr == t2->clausenr) result = runestrcmp(t1->text, t2->text); else result = Compare(t1->clausenr, t2->clausenr); break; case FloatTerm: result = Compare(t1->dval, t2->dval); break; case IntegerTerm: result = Compare(t1->ival, t2->ival); 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 *goal, Binding **bindings, Module *module) { USED(module); 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 *goal, Binding **bindings, Module *module) { USED(module); Term *term = goal->children; Term *name = term->next; Term *arity = name->next; if(term->tag == VariableTerm && name->tag == VariableTerm) Throw(instantiationerror()); if(term->tag == VariableTerm && arity->tag == VariableTerm) Throw(instantiationerror()); if(term->tag == VariableTerm && !(name->tag == VariableTerm || name->tag == AtomTerm || name->tag == IntegerTerm || name->tag == FloatTerm)) Throw(typeerror(L"atomic", name)); if(term->tag == VariableTerm && !(arity->tag == VariableTerm || arity->tag == IntegerTerm)) Throw(typeerror(L"integer", arity)); if(term->tag == VariableTerm && name->tag != VariableTerm && name->tag != AtomTerm && arity->tag == IntegerTerm && arity->ival > 0) Throw(typeerror(L"atom", name)); if(term->tag == VariableTerm && arity->tag == IntegerTerm && arity->ival < 0) Throw(domainerror(L"not_less_than_zero", arity)); if(term->tag == VariableTerm){ if(arity->ival == 0) return unify(term, name, bindings); else{ /* Make arity many fresh variables */ int i; Term *args = nil; for(i = 0; i < arity->ival; i++){ Term *arg = mkvariable(L"_"); args = appendterm(args, arg); } Term *realterm = mkcompound(name->text, arity->ival, args); return unify(term, realterm, bindings); } }else{ Rune *namestr; int arityint; if(term->tag == CompoundTerm){ namestr = term->text; arityint = term->arity; }else{ namestr = prettyprint(term, 0, 0, 0); arityint = 0; } Term *realname = mkatom(namestr); Term *realarity = mkinteger(arityint); if(unify(name, realname, bindings) && unify(arity, realarity, bindings)) return 1; } return 0; } int builtinarg(Term *goal, Binding **bindings, Module *module) { USED(module); Term *n = goal->children; Term *term = n->next; Term *arg = term->next; if(n->tag == VariableTerm || term->tag == VariableTerm) Throw(instantiationerror()); if(n->tag != IntegerTerm) Throw(typeerror(L"integer", n)); if(term->tag != CompoundTerm) Throw(typeerror(L"compound", term)); if(n->ival < 0) Throw(domainerror(L"not_less_than_zero", n)); if(n->ival > term->arity) return 0; int i; Term *t; for(i = 1, 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 *goal, Binding **bindings, Module *module) { USED(module); Term *term = goal->children; Term *list = term->next; if(term->tag == VariableTerm && ispartiallist(list)) Throw(instantiationerror()); if(!(ispartiallist(list) || islist(list))) Throw(typeerror(L"list", list)); Term *head = listhead(list); Term *tail = listtail(list); if(term->tag == VariableTerm && head->tag == VariableTerm) Throw(instantiationerror()); if(islist(list) && !(head->tag == AtomTerm || head->tag == VariableTerm) && !isemptylist(tail)) Throw(typeerror(L"atom", head)); if(islist(list) && head->tag == CompoundTerm && isemptylist(tail)) Throw(typeerror(L"atomic", head)); if(term->tag == VariableTerm && isemptylist(list)) Throw(domainerror(L"non_empty_list", list)); 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->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) { USED(module); 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 *goal, Binding **bindings, Module *module) { USED(bindings); Term *catchgoal = goal->children; Term *catcher = catchgoal->next; Term *recover = catcher->next; Goal *catchframe = malloc(sizeof(Goal)); catchframe->goal = recover; catchframe->module = module; catchframe->catcher = catcher; catchframe->next = goalstack; goalstack = catchframe; Goal *g = malloc(sizeof(Goal)); g->goal = catchgoal; g->module = module; g->catcher = nil; g->next = goalstack; goalstack = g; return 1; } int builtinthrow(Term *goal, Binding **bindings, Module *module) { USED(bindings); USED(module); Term *ball = goal->children; print("Throwing: %S\n", prettyprint(ball, 0, 0, 0)); Goal *g; for(g = goalstack; 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, 0, 0, 0)); exits("exception"); return 0; }else{ goalstack = g->next; Goal *newgoal = malloc(sizeof(Goal)); newgoal->goal = copyterm(g->goal, nil); newgoal->module = module; newgoal->catcher = nil; newgoal->next = goalstack; goalstack = 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 *goal, Binding **bindings, Module *module) { USED(goal); USED(bindings); USED(module); return 0; } int builtinsetprologflag(Term *goal, Binding **bindings, Module *module) { USED(bindings); USED(module); 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 *goal, Binding **bindings, Module *module) { USED(bindings); USED(module); 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 *goal, Binding **bindings, Module *module) { USED(bindings); USED(module); 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 != IntegerTerm && 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 *goal, Binding **bindings, Module *module) { USED(bindings); USED(module); Term *stream = goal->children; if(stream->tag != VariableTerm && stream->tag != IntegerTerm) Throw(domainerror(L"stream", stream)); Term *current = currentinputstream(); return unify(stream, current, bindings); } int builtincurrentoutput(Term *goal, Binding **bindings, Module *module) { USED(bindings); USED(module); Term *stream = goal->children; if(stream->tag != VariableTerm && stream->tag != IntegerTerm) Throw(domainerror(L"stream", stream)); Term *current = currentoutputstream(); return unify(stream, current, bindings); } int builtinsetinput(Term *goal, Binding **bindings, Module *module) { USED(bindings); USED(module); Term *stream = goal->children; if(stream->tag == VariableTerm) Throw(instantiationerror()); if(stream->tag != IntegerTerm && 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 *goal, Binding **bindings, Module *module) { USED(bindings); USED(module); Term *stream = goal->children; if(stream->tag == VariableTerm) Throw(instantiationerror()); if(stream->tag != IntegerTerm && 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; } int builtinreadterm(Term *goal, Binding **bindings, Module *module) { USED(bindings); USED(module); Term *stream = goal->children; Term *term = stream->next; Term *options = term->next; if(stream->tag == VariableTerm) Throw(instantiationerror()); if(options->tag != AtomTerm || runestrcmp(options->text, L"[]") != 0) Throw(typeerror(L"empty_list", options)); if(stream->tag != IntegerTerm && stream->tag != AtomTerm) Throw(domainerror(L"stream_or_alias", stream)); if(!isopenstream(stream)) Throw(existenceerror(L"stream", stream)); if(isoutputstream(stream)) Throw(permissionerror(L"input", L"stream", stream)); if(isbinarystream(stream)) Throw(permissionerror(L"input", L"binary_stream", stream)); Term *realterm; int error = readterm(stream, options, &realterm); if(error) Throw(realterm); return unify(term, realterm, bindings); } int builtinwriteterm(Term *goal, Binding **bindings, Module *module) { USED(bindings); USED(module); Term *stream = goal->children; Term *term = stream->next; Term *options = term->next; if(stream->tag == VariableTerm) Throw(instantiationerror()); if(options->tag != AtomTerm || runestrcmp(options->text, L"[]") != 0) Throw(typeerror(L"empty_list", options)); if(stream->tag != IntegerTerm && stream->tag != AtomTerm) Throw(domainerror(L"stream_or_alias", stream)); if(!isopenstream(stream)) Throw(existenceerror(L"stream", stream)); if(isinputstream(stream)) Throw(permissionerror(L"output", L"stream", stream)); if(isbinarystream(stream)) Throw(permissionerror(L"output", L"binary_stream", stream)); writeterm(stream, options, term); return 1; }