/* YAP support for some low-level SWI stuff */ #include #include "pl-incl.h" static atom_t uncachedCodeToAtom(int chrcode) { if ( chrcode < 256 ) { char tmp[1]; tmp[0] = chrcode; return lookupAtom(tmp, 1); } else { pl_wchar_t tmp[2]; int new; tmp[0] = chrcode; tmp[1] = '\0'; return (atom_t)YAP_LookupWideAtom(tmp); } } atom_t codeToAtom(int chrcode) { atom_t a; if ( chrcode == EOF ) return ATOM_end_of_file; assert(chrcode >= 0); if ( chrcode < (1<<15) ) { int page = chrcode / 256; int entry = chrcode % 256; atom_t *pv; if ( !(pv=GD->atoms.for_code[page]) ) { pv = PL_malloc(256*sizeof(atom_t)); memset(pv, 0, 256*sizeof(atom_t)); GD->atoms.for_code[page] = pv; } if ( !(a=pv[entry]) ) { a = pv[entry] = uncachedCodeToAtom(chrcode); } } else { a = uncachedCodeToAtom(chrcode); } return a; } int PL_rethrow(void) { GET_LD if ( LD->exception.throw_environment ) longjmp(LD->exception.throw_environment->exception_jmp_env, 1); fail; } int callProlog(module_t module, term_t goal, int flags, term_t *ex) { term_t g = PL_new_term_ref(); functor_t fd; predicate_t proc; if ( ex ) *ex = 0; PL_strip_module(goal, &module, g); if ( !PL_get_functor(g, &fd) ) { PL_error(NULL, 0, NULL, ERR_TYPE, ATOM_callable, goal); if ( ex ) *ex = exception_term; fail; } proc = PL_pred(fd, module); { int arity = arityFunctor(fd); term_t args = PL_new_term_refs(arity); qid_t qid; int n, rval; for(n=0; ntype) { case V_INTEGER: succeed; #ifdef O_GMP case V_MPZ: succeed; case V_MPQ: /* never from stacks iff integer */ if ( mpz_cmp_ui(mpq_denref(n->value.mpq), 1L) == 0 ) { mpz_clear(mpq_denref(n->value.mpq)); n->value.mpz[0] = mpq_numref(n->value.mpq)[0]; n->type = V_MPZ; succeed; } fail; #endif case V_REAL: if ( (flags & TOINT_CONVERT_FLOAT) ) { if ( double_in_int64_range(n->value.f) ) { int64_t l = (int64_t)n->value.f; if ( (flags & TOINT_TRUNCATE) || (double)l == n->value.f ) { n->value.i = l; n->type = V_INTEGER; return TRUE; } return FALSE; #ifdef O_GMP } else { mpz_init_set_d(n->value.mpz, n->value.f); n->type = V_MPZ; return TRUE; #endif } } return FALSE; } #endif assert(0); fail; } int _PL_unify_atomic(term_t t, PL_atomic_t a) { return YAP_Unify(Yap_GetFromSlot(t), (YAP_Term)a); } word lookupAtom(const char *s, size_t len) { return (word)YAP_LookupAtom(s); } atom_t lookupUCSAtom(const pl_wchar_t *s, size_t len) { return (atom_t)YAP_LookupWideAtom(s); } /******************************* * OPTIONS * *******************************/ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Variable argument list: atom_t name int type OPT_ATOM, OPT_STRING, OPT_BOOL, OPT_INT, OPT_LONG pointer value - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #define MAXOPTIONS 32 typedef union { bool *b; /* boolean value */ long *l; /* long value */ int *i; /* integer value */ char **s; /* string value */ word *a; /* atom value */ term_t *t; /* term-reference */ void *ptr; /* anonymous pointer */ } optvalue; bool scan_options(term_t options, int flags, atom_t optype, const opt_spec *specs, ...) { va_list args; const opt_spec *s; optvalue values[MAXOPTIONS]; term_t list = PL_copy_term_ref(options); term_t head = PL_new_term_ref(); term_t tmp = PL_new_term_ref(); term_t val = PL_new_term_ref(); int n; if ( trueFeature(ISO_FEATURE) ) flags |= OPT_ALL; va_start(args, specs); for( n=0, s = specs; s->name; s++, n++ ) values[n].ptr = va_arg(args, void *); va_end(args); while ( PL_get_list(list, head, list) ) { atom_t name; int arity; if ( PL_get_name_arity(head, &name, &arity) ) { if ( name == ATOM_equals && arity == 2 ) { PL_get_arg(1, head, tmp); if ( !PL_get_atom(tmp, &name) ) goto itemerror; PL_get_arg(2, head, val); } else if ( arity == 1 ) { PL_get_arg(1, head, val); } else if ( arity == 0 ) PL_put_atom(val, ATOM_true); } else if ( PL_is_variable(head) ) { return PL_error(NULL, 0, NULL, ERR_INSTANTIATION); } else { itemerror: return PL_error(NULL, 0, NULL, ERR_DOMAIN, optype, head); } for( n=0, s = specs; s->name; n++, s++ ) { if ( s->name == name ) { switch((s->type & OPT_TYPE_MASK)) { case OPT_BOOL: { atom_t aval; if ( !PL_get_atom(val, &aval) ) fail; if ( aval == ATOM_true || aval == ATOM_on ) *values[n].b = TRUE; else if ( aval == ATOM_false || aval == ATOM_off ) *values[n].b = FALSE; else goto itemerror; break; } case OPT_INT: { if ( !PL_get_integer(val, values[n].i) ) goto itemerror; break; } case OPT_LONG: { if ( !PL_get_long(val, values[n].l) ) { if ( (s->type & OPT_INF) && PL_is_inf(val) ) *values[n].l = LONG_MAX; else goto itemerror; } break; } case OPT_NATLONG: { if ( !PL_get_long(val, values[n].l) ) goto itemerror; if ( *(values[n].l) <= 0 ) return PL_error(NULL, 0, NULL, ERR_DOMAIN, ATOM_not_less_than_one, val); break; } case OPT_STRING: { char *str; if ( !PL_get_chars(val, &str, CVT_ALL) ) /* copy? */ goto itemerror; *values[n].s = str; break; } case OPT_ATOM: { atom_t a; if ( !PL_get_atom(val, &a) ) goto itemerror; *values[n].a = a; break; } case OPT_TERM: { *values[n].t = val; val = PL_new_term_ref(); /* can't reuse anymore */ break; } default: assert(0); fail; } break; } } if ( !s->name && (flags & OPT_ALL) ) goto itemerror; } if ( !PL_get_nil(list) ) return PL_error(NULL, 0, NULL, ERR_TYPE, ATOM_list, list); succeed; } int get_atom_ptr_text(Atom a, PL_chars_t *text) { if (YAP_IsWideAtom(a)) { pl_wchar_t *name = (pl_wchar_t *)YAP_WideAtomName(a); text->text.w = name; text->length = wcslen(name); text->encoding = ENC_WCHAR; } else { char *name = (char *)YAP_AtomName(a); text->text.t = name; text->length = strlen(name); text->encoding = ENC_ISO_LATIN_1; } text->storage = PL_CHARS_HEAP; text->canonical = TRUE; succeed; } int get_atom_text(atom_t atom, PL_chars_t *text) { Atom a = atomValue(atom); return get_atom_ptr_text(a, text); } int get_string_text(word w, PL_chars_t *text ARG_LD) { fail; } void PL_get_number(term_t l, number *n) { YAP_Term t = valHandle(l); if (YAP_IsIntTerm(t)) { n->type = V_INTEGER; n->value.i = YAP_IntOfTerm(t); #ifdef O_GMP } else { n->type = V_MPZ; n->value.mpz = YAP_BigNumOfTerm(t); #endif } } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Formatting a float. This is very complicated as we must write floats such that it can be read as a float. This means using the conventions of the C locale and if the float happens to be integer as .0. Switching the locale is no option as locale handling is not thread-safe and may have unwanted consequences for embedding. There is a intptr_t discussion on the very same topic on the Python mailinglist. Many hacks are proposed, none is very satisfactory. Richard O'Keefe suggested to use ecvt(), fcvt() and gcvt(). These are not thread-safe. The GNU C library provides *_r() variations that can do the trick. An earlier patch used localeconv() to find the decimal point, but this is both complicated and not thread-safe. Finally, with help of Richard we decided to replace the first character that is not a digit nor [eE], as this must be the decimal point. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #define isDigit(c) ((c) >= '0' && (c) <= '9') char * format_float(double f, char *buf, const char *format) { char *q; sprintf(buf, format, f); q = buf; if ( *q == '-' ) /* skip -?[0-9]* */ q++; while(*q && (isDigit(*q) || *q <= ' ')) q++; switch( *q ) { case '\0': *q++ = '.'; *q++ = '0'; *q = EOS; break; case 'e': case 'E': break; default: *q = '.'; } return buf; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - codes_or_chars_to_buffer(term_t l, unsigned flags, int wide) If l represents a list of codes or characters, return a buffer holding the characters. If wide == TRUE the buffer contains objects of type pl_wchar_t. Otherwise it contains traditional characters. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ static int charCode(YAP_Term w) { if ( YAP_IsAtomTerm(w) ) { Atom a = atomValue(w); if ( YAP_AtomNameLength(a) == 1) { if (YAP_IsWideAtom(a)) { return YAP_WideAtomName(a)[0]; } return YAP_AtomName(a)[0]; } } return -1; } Buffer codes_or_chars_to_buffer(term_t l, unsigned int flags, int wide) { GET_LD Buffer b; YAP_Term list = YAP_GetFromSlot(l); YAP_Term arg; enum { CHARS, CODES } type; if ( YAP_IsPairTerm(list) ) { arg = YAP_HeadOfTerm(list); if ( YAP_IsIntTerm(arg) ) { long int i = YAP_IntOfTerm(arg); if ( i >= 0 && (wide || i < 256) ) { type = CODES; goto ok; } } else if ( charCode(arg) >= 0 ) { type = CHARS; goto ok; } } else if ( list != YAP_TermNil() ) { return findBuffer(flags); } fail; ok: b = findBuffer(flags); while( YAP_IsPairTerm(list) ) { intptr_t c = -1; arg = YAP_HeadOfTerm(list); switch(type) { case CODES: if ( YAP_IsIntTerm(arg) ) { c = YAP_IntOfTerm(arg); } break; case CHARS: c = charCode(arg); break; } if ( c < 0 || (!wide && c > 0xff) ) { unfindBuffer(flags); /* TBD: check unicode range */ return NULL; } if ( wide ) addBuffer(b, (pl_wchar_t)c, pl_wchar_t); else addBuffer(b, (unsigned char)c, unsigned char); list = YAP_TailOfTerm(list); } if ( list != YAP_TermNil() ) { unfindBuffer(flags); return NULL; } return b; }