1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131 |
- /* Definitions for high-level IO functions.
- This file is part of khipu.
- khipu is free software: you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>. */
- #include <cctype>
- #include <cstdlib>
- #include <cstdio>
- #include <cstdarg>
- #include <cerrno>
- #include <new>
- #include "khipu.hpp"
- #include "utils/chmask.hpp"
- #include "utils/raw_acc.hpp"
- KP_DECLS_BEGIN
- static inline exception
- raise_eos (interpreter *interp)
- {
- return (interp->raise ("parse-error", "read: premature end of input"));
- }
- enum
- {
- TOK_NONE,
- TOK_OPEN,
- TOK_CLOSE,
- TOK_DOT,
- TOK_SYM,
- TOK_NUM,
- TOK_CHAR,
- TOK_COMMA,
- TOK_COMMAAT,
- TOK_COMMADOT,
- TOK_BQ,
- TOK_QUOTE,
- TOK_SHARPDOT,
- TOK_LABEL,
- TOK_BACKREF,
- TOK_SHARPQUOTE,
- TOK_SHARPOPEN,
- TOK_OPENB,
- TOK_CLOSEB,
- TOK_OPENBRACE,
- TOK_CLOSEBRACE,
- TOK_SHARPSYM,
- TOK_GENSYM,
- TOK_DQUOTE,
- TOK_SHARPDQUOT
- };
- static inline bool
- symchar_p (int c)
- {
- static const char SPEC_CHS[] = "()[]{}'\";`,\\| \f\n\r\t\v";
- return (!memchr (SPEC_CHS, c, sizeof (SPEC_CHS) - 1));
- }
- static result<bool>
- numtok_p (interpreter *interp, char *tok, int len)
- {
- if (*tok == '\0')
- return (false);
- else if (len == 4 && (memcmp (tok, "-INF", 4) == 0 ||
- memcmp (tok, "+INF", 4) == 0))
- {
- interp->retval = *tok == '-' ? FLT_NINF : FLT_PINF;
- return (true);
- }
- else if (len == 3 && memcmp (tok, "NaN", 3) == 0)
- {
- interp->retval = FLT_QNAN;
- return (true);
- }
- num_info info;
- if (tok[len - 1] == '.')
- tok[len++] = '0';
- if (parse_num (interp, tok, len, info) < 0)
- return (false);
- else if (info.type == typecode::INT)
- {
- int slen = info.dec_end - info.dec_start;
- int rl = invbsize (info.radix, slen) + 1;
- if (rl == 1)
- { // May fit in a fixint.
- intptr_t uval = strtoui1 (tok + info.dec_start, slen, info.radix);
- intptr_t val = info.sign ? -uval : uval;
- interp->retval = intobj (interp, val);
- }
- else
- {
- bigint *lp = as_bigint (alloc_bigint (interp, rl));
- lp->len = strtoui (lp->data, tok + info.dec_start, slen, info.radix);
- #ifdef KP_ARCH_WIDE
- interp->retval = lp->as_obj () | (info.sign ? SIGN_BIT : 0);
- #else
- if (info.sign)
- lp->len = -lp->len;
- interp->retval = lp->as_obj ();
- #endif
- }
- return (true);
- }
- // info.type == typecode::FLOAT
- int expo, slen = info.frac_end - info.dec_start - 1;
- if (info.expo_start != 0)
- {
- uint32_t uev;
- if (invbsize (info.radix, len - info.expo_start) > 1 ||
- (uev = strtoui1 (&tok[info.expo_start], len -
- info.expo_start, info.radix)) >
- 0x7fffffffu + info.expo_sign)
- return (interp->raise ("arith-error", "exponent "
- "too large in floating point value"));
- expo = info.expo_sign ? -(int)uev : uev;
- }
- else
- expo = 0;
- if (info.frac_end != 0)
- expo -= info.frac_end - info.dec_end - info.got_dot;
- /* The maximum number of limbs to be used by 'strtolf' is the
- * sum of the limbs needed by the mantissa and radix^exponent,
- * given that it may end up multiplying them. */
- int nlimbs = invbsize (info.radix, slen) +
- invbsize (info.radix, abs (expo)) + 2;
- tmp_allocator ta { interp };
- limb_t *mp = (limb_t *)ta.alloc (nlimbs * sizeof (*mp));
- memset (mp, 0, nlimbs * sizeof (*mp));
- int ret = strtolf (interp, tok, len, info, expo, mp);
- int b2exp = (expo - ret) * LIMB_BITS;
- if (b2exp >= DBL_MIN_EXP && b2exp <= DBL_MAX_EXP)
- { // See if we can fit it in a double.
- double dbl = uitodbl (mp, ret, expo);
- if (!finf_p (dbl))
- {
- interp->retval = fltobj::make (interp,
- info.sign ? -dbl : dbl);
- return (true);
- }
- }
- bigfloat *fp = as_bigfloat (alloc_bigfloat (interp, ret));
- memcpy (fp->data, mp, (fp->len = ret) * sizeof (*fp->data));
- fp->expo = expo;
- #ifdef KP_ARCH_WIDE
- interp->retval = fp->as_obj () | (info.sign ? SIGN_BIT : 0);
- #else
- if (info.sign)
- fp->len = -fp->len;
- interp->retval = fp->as_obj ();
- #endif
- return (true);
- }
- static const struct
- {
- const char *name;
- uint32_t value;
- } CHAR_NAMES[] =
- {
- { "lf", '\n' },
- { "tab", '\t' },
- { "sp", ' ' },
- { "nil", '\0' },
- { "bell", '\a' },
- { "back", '\b' },
- { "ret", '\r' },
- { "vtab", '\v' }
- };
- const char* chobj_repr (uint32_t ch)
- {
- for (size_t i = 0; i < KP_NELEM (CHAR_NAMES); ++i)
- if (ch == CHAR_NAMES[i].value)
- return (CHAR_NAMES[i].name);
- return (nullptr);
- }
- reader::reader (interpreter *ip, object input, package *pkg) :
- interp (ip), pairs_valref (ip), ipkg (pkg)
- {
- this->pairs.local_init (this->stpairs, KP_NELEM (this->stpairs));
- this->pair_cnt = 0;
- for (uint32_t i = 0; i < this->pairs.len; ++i)
- this->pairs.data[i] = UNBOUND;
- this->bufmax = KP_NELEM (this->stbuf);
- this->bufp = this->stbuf;
- this->take ();
- this->src = as_stream (input);
- *this->pairs_valref = this->pairs.as_obj ();
- if (!this->ipkg)
- this->ipkg = as_package (ip->xpkg);
- }
- void reader::take ()
- {
- this->toktype = TOK_NONE;
- this->bufcnt = 0;
- }
- void reader::push_ch (const schar& ch)
- {
- if (this->bufcnt + ch.len >= this->bufmax)
- {
- int nsize = (int)upsize (this->bufcnt + ch.len + 1);
- char *nbuf = (char *)xmalloc (nsize);
- memcpy (nbuf, this->bufp, this->bufcnt);
- if (this->bufp != this->stbuf)
- xfree (this->bufp);
- this->bufp = nbuf;
- this->bufmax = nsize;
- }
- fscpy (this->bufp + this->bufcnt, ch.buf, ch.len);
- this->bufcnt += ch.len;
- }
- result<bool> reader::read_token (schar& ch, int digs)
- {
- bool first = true;
- int esc_p = 0, sym_p = 0;
- while (true)
- {
- if (!first)
- {
- bool rv = KP_TRY (this->src->sgetc (this->interp, ch));
- if (!rv)
- goto term;
- }
- first = false;
- if (ch.uc == '|')
- esc_p ^= (sym_p = 1);
- else if (ch.uc == '\\')
- {
- sym_p = 1;
- bool rv = KP_TRY (this->src->sgetc (this->interp, ch));
- if (!rv)
- goto term;
- this->push_ch (ch);
- }
- else if (!esc_p && (!symchar_p (ch.uc) &&
- (!digs || isdigit (ch.uc))))
- break;
- else
- this->push_ch (ch);
- }
- this->src->ungetuc (ch.buf, ch.len);
- term:
- this->bufp[this->bufcnt] = '\0';
- return (sym_p != 0);
- }
- void reader::expand ()
- {
- object *p2 = (object *)xmalloc (this->pairs.len * 2);
- copy_objs (p2, this->pairs.data, this->pairs.len);
- this->pairs.len *= 2;
- if (this->pairs.data != this->stpairs)
- xfree (this->pairs.data);
- this->pairs.data = p2;
- }
- object reader::getlbl (object lbl) const
- {
- for (uint32_t i = 0; i < this->pair_cnt; i += 2)
- if (this->pairs.data[i] == lbl)
- return (this->pairs.data[i + 1]);
- return (UNBOUND);
- }
- object* reader::putlbl (object lbl)
- {
- for (uint32_t i = 0; i < this->pair_cnt; i += 2)
- if (this->pairs.data[i] == UNBOUND)
- {
- this->pairs.data[i] = lbl;
- return (&this->pairs.data[i + 1]);
- }
- else if (this->pairs.data[i] == lbl)
- return (&this->pairs.data[i + 1]);
- if (this->pair_cnt == this->pairs.len)
- this->expand ();
- this->pairs.data[this->pair_cnt++] = lbl;
- return (&this->pairs.data[this->pair_cnt]);
- }
- result<bool> reader::nextc (schar& ch)
- {
- do
- {
- bool rv = KP_TRY (this->src->sgetc (this->interp, ch));
- if (!rv)
- return (false);
- else if (ch.uc == ';')
- do
- {
- rv = KP_TRY (this->src->sgetc (this->interp, ch));
- if (!rv)
- return (false);
- }
- while (ch.uc != '\n');
- if (ch.uc == '\n')
- ++this->lineno;
- }
- while (isspace (ch.uc));
- return (true);
- }
- static inline bool
- check_symname (interpreter *interp, const char *name, int len)
- {
- num_info info;
- return (parse_num (interp, name, len, info) < 0);
- }
- static result<void>
- update_pkg_expr (interpreter *interp, const char *sname,
- const char *uptr, const char *uend, object& out)
- {
- valref key = KP_TRY (symbol::make_kword (interp, uptr, uend - uptr));
- if (out == UNBOUND)
- {
- out = KP_TRY (KP_CALL (interp, list_fct,
- intern (interp, sname, (uptr - sname) - 2), *key));
- }
- else
- {
- *key = KP_TRY (cons::make (interp, *key, NIL));
- out = KP_TRY (cons::make (interp, out, *key));
- }
- return (0);
- }
- static result<object>
- make_pkg_expr (interpreter *interp, object name, object ipkg)
- {
- /* Transform a symbol name of the kind 'a::b::c' into its equivalent
- * expression: ((a :b) :c).
- * This is required when the symbol can't be resolved at read-time,
- * and is therefore delegated to a runtime lookup. */
- const char *sname = str_cdata (name);
- int len = as_str(name)->nbytes;
- valref ret (interp, UNBOUND), key (interp, UNBOUND);
- while (true)
- {
- auto uptr = (const char *)memchr (sname, ':', len);
- if (!uptr)
- { // Last name - Append and we're out.
- *key = KP_TRY (symbol::make_kword (interp, sname, len));
- *key = KP_TRY (cons::make (interp, *key, NIL));
- *ret = KP_TRY (cons::make (interp, *ret, *key));
- break;
- }
- else if (uptr[1] != *uptr)
- return (interp->raise ("parse-error",
- KP_SPRINTF (interp,
- "read: invalid symbol name: %Q",
- name)));
- auto u2 = (const char *)memchr (uptr + 2, ':', (sname + len - 2) - uptr);
- if (!u2)
- {
- KP_VTRY (update_pkg_expr (interp, sname, uptr + 2,
- sname + len, *ret));
- break;
- }
- else if (u2[1] != *u2)
- return (interp->raise ("parse-error",
- KP_SPRINTF (interp,
- "read: invalid symbol name: %Q",
- name)));
- KP_VTRY (update_pkg_expr (interp, sname, uptr + 2, u2, *ret));
- len -= (u2 + 2) - sname;
- sname = u2 + 2;
- }
- kp_return (*ret);
- }
- result<void> reader::handle_sym (object pkg, object name)
- {
- valref xpkg (interp, pkg), tmp (interp, UNBOUND);
- const char *sname = str_cdata (name);
- int len = as_str(name)->nbytes;
- auto ip = this->interp;
- while (true)
- {
- auto uptr = (const char *)memchr (sname, ':', len);
- if (!uptr)
- {
- KP_VTRY (intern (this->interp, sname, len, as_package (*xpkg)));
- return (0);
- }
- else if (uptr[1] != *uptr ||
- !check_symname (interp, sname, uptr - sname))
- return (ip->raise ("parse-error",
- KP_SPRINTF (ip, "read: invalid symbol name: %Q",
- name)));
- *tmp = find_sym (interp, *xpkg, sname, uptr - sname);
- *tmp = !symbol_p (*tmp) ? UNBOUND : symval (*tmp);
- if (package_p (*tmp))
- // Still reachable at read-time - Update the current package.
- *xpkg = *tmp;
- else
- {
- KP_VTRY (make_pkg_expr (interp, name, *xpkg));
- return (0);
- }
- len -= (uptr + 2) - sname;
- sname = uptr + 2;
- }
- }
- result<uint32_t> reader::peek ()
- {
- schar ch;
- bool rv;
- if (this->toktype != TOK_NONE)
- return (this->toktype);
- rv = KP_TRY (this->nextc (ch));
- if (!rv)
- return (TOK_NONE);
- switch (ch.uc)
- {
- #define DISPATCH(ch, tok) \
- case ch: \
- this->toktype = TOK_##tok; \
- break
- DISPATCH ('(', OPEN);
- DISPATCH (')', CLOSE);
- DISPATCH ('[', OPENB);
- DISPATCH (']', CLOSEB);
- DISPATCH ('{', OPENBRACE);
- DISPATCH ('}', CLOSEBRACE);
- DISPATCH ('\'', QUOTE);
- DISPATCH ('`', BQ);
- DISPATCH (',', COMMA);
- DISPATCH ('"', DQUOTE);
- DISPATCH ('\\', CHAR);
-
- case '#':
- {
- rv = KP_TRY (this->src->sgetc (this->interp, ch));
- if (!rv)
- return (this->interp->raise ("parse-error",
- "read: invalid read macro"));
- else if (ch.uc == '.')
- this->toktype = TOK_SHARPDOT;
- else if (ch.uc == '\'')
- this->toktype = TOK_SHARPQUOTE;
- else if (ch.uc == '(')
- this->toktype = TOK_SHARPOPEN;
- else if (ch.uc == '<')
- return (this->interp->raise ("parse-error",
- "read: unreadable object"));
- else if (ch.uc == ':')
- {
- rv = KP_TRY (this->src->sgetc (this->interp, ch));
- if (!rv)
- return (raise_eos (this->interp));
- KP_VTRY (this->read_token (ch, 0));
- bool rv = KP_TRY (numtok_p (this->interp, this->bufp,
- this->bufcnt));
- if (rv)
- return (this->interp->raise ("parse-error",
- "read: invalid syntax after #: "
- "reader macro"));
- this->toktype = TOK_SYM;
- valref tmp = KP_TRY (alloc_sym (interp));
- symname(*tmp) = KP_TRY (string::make (this->interp, this->bufp,
- this->bufcnt));
- }
- else if (isdigit (ch.uc))
- {
- KP_VTRY (this->read_token (ch, 1),
- this->src->sgetc (this->interp, ch));
- if (ch.uc == '#')
- this->toktype = TOK_BACKREF;
- else if (ch.uc == '=')
- this->toktype = TOK_LABEL;
- else
- return (this->interp->raise ("parse-error",
- "read: invalid label"));
- errno = 0;
- char *endp;
- long xv = strtol (bufp, &endp, 10);
- if (*endp != '\0' || errno != 0)
- return (this->interp->raise ("parse-error",
- "read: invalid label"));
- this->interp->retval = fixint (xv);
- }
- else if (ch.uc == '!')
- {
- do
- KP_VTRY (this->src->sgetc (this->interp, ch));
- while (ch.uc != UEOF && ch.uc != '\n');
- return (this->peek ());
- }
- else if (ch.uc == '"')
- this->toktype = TOK_SHARPDQUOT;
- else if (ch.uc == '|')
- {
- for (int lvl = 1 ; ; )
- {
- KP_VTRY (this->src->sgetc (this->interp, ch));
- got_hashp:
- if (ch.uc == UEOF)
- return (raise_eos (this->interp));
- else if (ch.uc == '|')
- {
- KP_VTRY (this->src->sgetc (this->interp, ch));
- if (ch.uc == '#')
- {
- if (--lvl == 0)
- break;
- continue;
- }
- goto got_hashp;
- }
- else if (ch.uc == '#')
- {
- KP_VTRY (this->src->sgetc (this->interp, ch));
- if (ch.uc == '|')
- ++lvl;
- else
- goto got_hashp;
- }
- }
- return (this->peek ());
- }
- else if (ch.uc == '\\')
- { // #\x => (intern ',(symname x))
- object sym = KP_TRY (this->read_sexpr (UNBOUND));
- if (!symbol_p (sym))
- return (this->interp->raise ("type-error",
- "expected a symbol after "
- "read macro #\\"));
- valref tmp (interp, sym);
- KP_VTRY (alloc_cons (this->interp, 2));
- xcar(this->interp->alval) = symbol::quote;
- xcadr(this->interp->alval) = symname (*tmp);
- *tmp = this->interp->alval;
- KP_VTRY (alloc_cons (this->interp, 2));
- xcar(this->interp->alval) = KP_TRY (intern (this->interp,
- "intern", 6));
- xcadr(this->interp->alval) = *tmp;
- this->interp->retval = this->interp->alval;
- this->toktype = TOK_SYM;
- }
- else
- return (this->interp->raise ("parse-error",
- "read: unknown read macro"));
- break;
- }
- default:
- // Number or symbol.
- rv = KP_TRY (this->read_token (ch, 0));
- if (!rv)
- {
- if (*this->bufp == '.' && this->bufp[1] == '\0')
- return (this->toktype = TOK_DOT);
- bool rv = KP_TRY (numtok_p (this->interp, this->bufp,
- this->bufcnt));
- if (rv)
- return (this->toktype = TOK_NUM);
- }
- this->toktype = TOK_SYM;
- if (*this->bufp == ':')
- {
- if (this->bufcnt > 1 && this->bufp[1] == ':')
- {
- local_varobj<string> nm;
- nm.local_init (this->bufp + 2);
- KP_VTRY (this->handle_sym (root_package, nm.as_obj ()));
- }
- else
- KP_VTRY (symbol::make_kword (this->interp, this->bufp + 1));
- }
- else
- {
- local_varobj<string> nm;
- nm.local_init (this->bufp);
- KP_VTRY (this->handle_sym (this->ipkg->as_obj (), nm.as_obj ()));
- }
- break;
- }
- #undef DISPATCH
- return (this->toktype);
- }
- result<object> reader::read_array (object lbl)
- {
- object dummy, *dstp = lbl != UNBOUND ? this->putlbl (lbl) : &dummy;
- raw_acc<array> ar (3);
- *dstp = ar.as_obj ();
- while (true)
- {
- uint32_t rv = KP_TRY (this->peek ());
- if (rv == TOK_CLOSEB)
- break;
- else if (!this->readable_p ())
- return (raise_eos (this->interp));
- object obj = KP_TRY (this->read_sexpr (UNBOUND));
- ar.add_obj (obj);
- }
- this->take ();
- this->interp->retval = ar.as_obj ();
- array *ap = ar.release ();
- if (ap->len > 0)
- gc_register (this->interp, ap, sizeof (*ap) + ar.alloc);
- else
- {
- xfree (ap);
- this->interp->retval = deref (alloc_array (this->interp, 0));
- }
- return (this->interp->retval);
- }
- result<object> reader::read_table (object)
- {
- sp_guard sg (this->interp);
- // Push equality and hash functions.
- KP_VTRY (this->interp->push (NIL),
- this->interp->push (NIL));
- while (true)
- {
- uint32_t rv = KP_TRY (this->peek ());
- if (rv == TOK_CLOSEBRACE)
- break;
- else if (!this->readable_p ())
- return (raise_eos (this->interp));
- object obj = KP_TRY (this->read_sexpr (UNBOUND));
- KP_VTRY (this->interp->push (obj));
- }
- this->take ();
- return (table_fct (this->interp, this->interp->stack + sg.sp,
- this->interp->stklen () - sg.sp));
- }
- result<object> reader::read_tuple (object lbl)
- {
- object dummy, *dstp = lbl != UNBOUND ? this->putlbl (lbl) : &dummy;
- valref key (interp, NIL), ret = KP_TRY (alloc_tuple (this->interp, NIL));
- *dstp = *ret;
- while (true)
- {
- uint32_t rv = KP_TRY (this->peek ());
- if (rv == TOK_CLOSE)
- break;
- else if (!this->readable_p ())
- return (raise_eos (this->interp));
- *key = KP_TRY (this->read_sexpr (UNBOUND));
- KP_VTRY (tuple_put (interp, *ret, *key, false));
- }
- this->take ();
- kp_return (*ret);
- }
- static inline int
- escape_char (int ch)
- {
- if (ch == 'n')
- return ('\n');
- else if (ch == 't')
- return ('\t');
- else if (ch == 'r')
- return ('\r');
- else if (ch == 'a')
- return ('\a');
- else if (ch == 'b')
- return ('\b');
- else if (ch == '\\' || ch == '"')
- return (ch);
- else if (ch == '0')
- return (0);
- else
- return (-1);
- }
- static inline exception
- raise_eilseq (interpreter *interp, const char *seq)
- {
- char buf[100];
- sprintf (buf, "read: invalid escape sequence: %s", seq);
- return (interp->raise ("parse-error", buf));
- }
- result<object> reader::read_bvector ()
- {
- raw_acc<bvector> bv (8);
- char ebuf[8];
- for (ebuf[0] = '\\' ; ; )
- {
- int byte = KP_TRY (this->src->getb (this->interp));
- if (byte < 0)
- return (raise_eos (this->interp));
- else if (byte == '"')
- break;
- else if (byte == '\\')
- {
- int b1 = 0, b2 = 0;
- byte = KP_TRY (this->src->getb (this->interp));
- if (byte < 0)
- return (raise_eos (this->interp));
- else if (byte == 'x')
- {
- b1 = KP_TRY (this->src->getb (this->interp));
- b2 = KP_TRY (this->src->getb (this->interp));
- if ((b1 | b2) < 0)
- return (raise_eos (this->interp));
- else if (!isxdigit (b1) || !isxdigit (b2))
- {
- ebuf[1] = 'x', ebuf[2] = b1;
- ebuf[3] = b2, ebuf[4] = 0;
- return (raise_eilseq (this->interp, ebuf));
- }
- byte = (b1 - '0') * 16 + (b2 - '0');
- }
- else if ((byte = escape_char (byte)) < 0)
- {
- ebuf[1] = byte, ebuf[2] = 0;
- return (raise_eilseq (this->interp, ebuf));
- }
- }
- #ifdef KP_LITTLE_ENDIAN
- bv.add_data (&byte, 1);
- #else
- unsigned char ub = (unsigned char)byte;
- bv.add_data (&ub, 1);
- #endif
- }
- bvector *ret = bv.release ();
- if (ret->nbytes > 0)
- {
- this->interp->retval = ret->as_obj ();
- ret->data[ret->nbytes] = 0;
- gc_register (this->interp, ret, sizeof (*ret) + bv.alloc);
- }
- else
- {
- xfree (ret);
- this->interp->retval = deref (alloc_bvector (this->interp, 0));
- }
- return (this->interp->retval);
- }
- result<object> reader::read_str ()
- {
- raw_acc<string> str (8);
- char buf[16];
- as_str(str.as_obj ())->hval = 0;
- as_str(str.as_obj ())->len = 0;
- for (buf[0] = '\\' ; ; )
- {
- schar ch;
- bool rv = KP_TRY (this->src->sgetc (this->interp, ch));
- if (!rv)
- return (raise_eos (this->interp));
- else if (ch.uc == '"')
- break;
- else if (ch.uc == '\\')
- {
- int n;
- rv = KP_TRY (this->src->sgetc (this->interp, ch));
- if (!rv)
- return (raise_eos (this->interp));
- else if ((ch.uc == 'x' && (n = 2)) ||
- (ch.uc == 'u' && (n = 4)) ||
- (ch.uc == 'U' && (n = 8)))
- {
- buf[1] = ch.uc;
- for (int i = 0; i < n; ++i)
- {
- rv = KP_TRY (this->src->sgetc (this->interp, ch));
- if (!rv)
- return (raise_eos (this->interp));
- buf[2 + i] = *ch.buf;
- if (!isxdigit (*ch.buf))
- {
- buf[3 + i] = 0;
- return (raise_eilseq (this->interp, buf));
- }
- }
- buf[2 + n] = 0;
- ch.uc = strtol (&buf[2], nullptr, 16);
- if (ch.uc > MAX_CHAR)
- return (raise_eilseq (this->interp, buf));
- ch.len = u32tou8 ((unsigned char *)ch.buf, ch.uc);
- }
- else
- {
- if ((n = escape_char (ch.uc)) < 0)
- {
- buf[1] = 'X', buf[2 + ch.len] = 0;
- memcpy (&buf[2], ch.buf, ch.len);
- return (raise_eilseq (this->interp, buf));
- }
- *ch.buf = n, ch.len = 1;
- }
- }
- str.add_data (ch.buf, ch.len);
- ++as_str(str.as_obj ())->len;
- }
- string *sp = str.release ();
- if (sp->len > 0)
- {
- sp->vo_full |= FLAGS_CONST;
- this->interp->retval = sp->as_obj ();
- sp->data[sp->nbytes] = '\0';
- gc_register (this->interp, sp, sizeof (*sp) + str.alloc);
- }
- else
- {
- xfree (sp);
- this->interp->retval = deref (alloc_str (this->interp, 0));
- }
- return (this->interp->retval);
- }
- result<object> reader::read_char ()
- {
- schar cv;
- auto ip = this->interp;
- for (bool got = false ; ; got = true)
- {
- schar tmp;
- bool rv = KP_TRY (this->src->sgetc (this->interp, tmp));
- if (!rv)
- return (raise_eos (this->interp));
- else if (tmp.uc <= 0x7f && !symchar_p (tmp.uc) && got)
- {
- this->src->ungetuc (tmp.buf, tmp.len);
- this->bufp[this->bufcnt] = '\0';
- break;
- }
- else if (!isspace (tmp.uc) && (symchar_p (tmp.uc) || !got))
- {
- if (!got)
- cv = tmp;
- this->push_ch (tmp);
- }
- else if (!got)
- return (ip->raise ("parse-error", "read: empty character designator"));
- else
- {
- this->bufp[this->bufcnt] = '\0';
- break;
- }
- }
- local_varobj<string> sn;
- if (this->bufp[1] == '\0')
- ;
- else if (cv.uc == 'u' || cv.uc == 'U' || cv.uc == 'x')
- {
- long rv = strtol (this->bufp + 1, 0, 16);
- if (errno != 0 || rv >= (long)MAX_CHAR)
- return (ip->raise ("parse-error", "read: invalid unicode constant"));
- cv.uc = (uint32_t)rv;
- }
- else if (cv.uc >= 'a' && cv.uc <= 'z')
- {
- cv.uc = ~0u;
- for (size_t i = 0; i < KP_NELEM (CHAR_NAMES); ++i)
- if (strcmp (this->bufp, CHAR_NAMES[i].name) == 0)
- {
- cv.uc = CHAR_NAMES[i].value;
- break;
- }
- if (cv.uc == ~0u)
- {
- sn.local_init (this->bufp);
- return (ip->raise ("parse-error",
- KP_SPRINTF (ip, "read: unknown character: \\%Q",
- sn.as_obj ())));
- }
- }
- else
- {
- sn.local_init (this->bufp);
- return (ip->raise ("parse-error",
- KP_SPRINTF (ip, "read: unknown character: \\%Q",
- sn.as_obj ())));
- }
- this->take ();
- kp_return (charobj (cv.uc));
- }
- result<object> reader::read_list (object lbl)
- {
- object dummy, *dstp = lbl != UNBOUND ? this->putlbl (lbl) : &dummy;
- valref lr (this->interp, NIL), elem (this->interp);
- bool dot = false;
- uint32_t tok = KP_TRY (this->peek ());
- if (tok == TOK_CLOSE)
- {
- this->take ();
- kp_return (*dstp = NIL);
- }
- else if (tok == TOK_SHARPQUOTE)
- {
- KP_VTRY (intern (this->interp, "apply", 5));
- *lr = KP_TRY (cons::make (this->interp, this->interp->retval, *lr));
- this->take ();
- }
- tok = KP_TRY (this->peek ());
- while (tok != TOK_CLOSE)
- {
- *elem = KP_TRY (this->read_sexpr (UNBOUND));
- *lr = KP_TRY (cons::make (this->interp, *elem, *lr));
- tok = KP_TRY (this->peek ());
- if (tok == TOK_DOT)
- {
- this->take ();
- *elem = KP_TRY (this->read_sexpr (UNBOUND));
- tok = KP_TRY (this->peek ());
- if (tok != TOK_CLOSE)
- return (this->interp->raise ("parse-error",
- "read: elements follow dot in list"));
- dot = true;
- break;
- }
- else if (!this->readable_p ())
- return (raise_eos (this->interp));
- }
- this->take ();
- if (dot)
- { *lr = KP_TRY (nrevconc (this->interp, *lr, *elem)); }
- else
- { *lr = KP_TRY (nreverse_L (this->interp, *lr)); }
- kp_return (*dstp = *lr);
- }
- // Backquote implementation.
- static bool
- bq_member (object elem, object lst)
- {
- for (; cons_p (lst); lst = xcdr (lst))
- if (xcar (lst) == elem)
- return (true);
- return (false);
- }
- static inline exception
- bq_nonlist_splice_err (interpreter *interp, bool dot)
- {
- char errmsg[] = "read: the syntax `,@form is invalid";
- if (dot)
- errmsg[19] = '.';
- return (interp->raise ("parse-error", errmsg));
- }
- result<object> reader::read_comma (object lbl)
- {
- if (this->bq_level <= 0)
- return (this->interp->raise ("parse-error",
- "read: more commas than backquotes"));
- this->unquoted = true;
- --this->bq_level;
- schar next;
- object head = symbol::comma;
- bool rv = KP_TRY (this->src->sgetc (this->interp, next));
- if (!rv)
- return (raise_eos (this->interp));
- else if (*next.buf == '@')
- head = symbol::comma_at;
- else if (*next.buf == '.')
- head = symbol::comma_dot;
- else
- this->src->ungetuc (next.buf, next.len);
- object obj = KP_TRY (this->read_sexpr (UNBOUND));
- KP_VTRY (alloc_cons (this->interp, 2));
- xcar(this->interp->alval) = head;
- xcadr(this->interp->alval) = obj;
- if (lbl != UNBOUND)
- *this->putlbl(lbl) = this->interp->alval;
- ++this->bq_level;
- kp_return (this->interp->alval);
- }
- result<object> reader::read_bq (object lbl)
- {
- bool prev = this->unquoted;
- this->unquoted = false;
- ++this->bq_level;
- object obj = KP_TRY (this->read_sexpr (UNBOUND));
- if (cons_p (obj))
- {
- object head = xcar (obj), tst = symbol::comma_at;
- if (head == symbol::comma_at || head == symbol::comma_dot)
- return (bq_nonlist_splice_err (interp, head == symbol::comma_dot));
- else if (bq_member (tst, obj) ||
- bq_member (tst = symbol::comma_dot, obj))
- {
- char errmsg[] = "read: the syntax `( ... . ,@form) is invalid";
- if (tst == symbol::comma_dot)
- errmsg[27] = '.';
- return (this->interp->raise ("parse-error", errmsg));
- }
- }
- else if (this->unquoted &&
- !(array_p (obj) || table_p (obj) || tuple_p (obj)))
- return (this->interp->raise ("parse-error",
- "read: unquote outside sequence"));
- KP_VTRY (alloc_cons (this->interp, 2));
- xcar(this->interp->alval) = symbol::backquote;
- xcadr(this->interp->alval) = obj;
- if (lbl != UNBOUND)
- *this->putlbl(lbl) = this->interp->alval;
- this->unquoted = prev;
- --this->bq_level;
- kp_return (this->interp->alval);
- }
- static const object BQ_NCONCABLE = fixint (0) | EXTRA_BIT;
- static result<object>
- bq_list (interpreter *interp, object form1)
- {
- return (KP_CALL (interp, list_fct, intern (interp, "list", 4), form1));
- }
- static result<object>
- bq_transform (interpreter *interp, object form)
- {
- if (!cons_p (form))
- {
- valref tmp = KP_TRY (expand_bq (interp, form));
- return (bq_list (interp, *tmp));
- }
-
- valref tmp (interp, xcar (form));
- if (*tmp == symbol::comma)
- {
- KP_VTRY (bq_list (interp, *tmp = xcadr (form)));
- return (interp->retval);
- }
- else if (*tmp == symbol::comma_at)
- kp_return (xcadr (form));
- else if (*tmp == symbol::comma_dot)
- {
- KP_VTRY (KP_CALL (interp, list_fct, BQ_NCONCABLE, xcadr (form)));
- return (interp->retval);
- }
- else if (*tmp == symbol::backquote)
- {
- *tmp = KP_TRY (KP_CALL (interp, list_fct, symbol::backquote,
- expand_bq (interp, xcadr (form))));
- KP_VTRY (bq_list (interp, *tmp));
- return (interp->retval);
- }
- else
- {
- *tmp = KP_TRY (expand_bq (interp, form));
- KP_VTRY (bq_list (interp, *tmp));
- return (interp->retval);
- }
- }
- static result<object>
- bq_expand_list (interpreter *interp, object forms)
- {
- valref ret (interp, NIL), tmp (interp, forms), tail (interp);
- while (*tmp != NIL)
- {
- KP_VTRY (bq_transform (interp, xcar (*tmp)));
- *ret = KP_TRY (cons::make (interp, interp->retval, *ret));
- *tail = xcdr (*tmp);
- if (*tail == NIL)
- break;
- else if (!xcons_p (*tail))
- {
- object tx = KP_TRY (KP_CALL (interp, list_fct,
- symbol::backquote, *tail));
- *ret = KP_TRY (cons::make (interp, tx, *ret));
- break;
- }
- else if (xcar (*tail) == symbol::comma)
- {
- *ret = KP_TRY (cons::make (interp, xcadr (*tail), *ret));
- break;
- }
- else if (xcar (*tail) == symbol::comma_at ||
- xcar (*tail) == symbol::comma_dot)
- return (bq_nonlist_splice_err (interp,
- xcar (*tail) == symbol::comma_dot));
- else
- *tmp = *tail;
- }
- kp_return (*ret);
- }
-
- static inline bool
- bq_splicing_p (interpreter *interp, object form)
- {
- valref tmp (interp, form);
- while (true)
- {
- if (!xcons_p (*tmp))
- return (false);
- else if (xcar (*tmp) == symbol::comma)
- *tmp = xcadr (*tmp);
- else
- break;
- }
-
- *tmp = xcar (*tmp);
- return (*tmp == symbol::comma_at || *tmp == symbol::comma_dot);
- }
- static inline result<object>
- bq_non_splicing (interpreter *interp, object form)
- {
- if (!bq_splicing_p (interp, form))
- kp_return (form);
- return (KP_CALL (interp, list_fct, intern (interp, "concat", 6), form));
- }
- static inline bool
- bq_cons_test (interpreter *interp, object form)
- {
- return (xcons_p (form) && xcar (form) == symbol::quote &&
- xcons_p (xcdr (form)) && xcddr (form) == NIL &&
- !bq_splicing_p (interp, xcadr (form)));
- }
- static result<object>
- bq_cons (interpreter *interp, object f1, object f2)
- {
- valref op = KP_TRY (intern (interp,
- bq_splicing_p (interp, f1) ? "list*" : "cons"));
- valref t2 (interp, f2), t1 (interp, f1);
-
- if (atom_p (*t2))
- return (KP_CALL (interp, list_fct, *op, *t1, *t2));
- object tmp = KP_TRY (intern (interp, "list", 4));
- if (xcar (*t2) == tmp)
- return (KP_CALL (interp, list_star, xcar (*t2), *t1, xcdr (*t2)));
- else if (bq_cons_test (interp, *t2) && bq_cons_test (interp, *t1))
- return (KP_CALL (interp, list_fct, symbol::quote,
- cons::make (interp, xcadr (*t1), xcadr (*t2))));
- else
- return (KP_CALL (interp, list_fct, *op, *t1, *t2));
- }
- static result<object>
- bq_append (interpreter *interp, object f1, object f2)
- {
- valref t1 (interp, f1), t2 (interp, f2), aux (interp, NIL);
-
- if (*t1 == NIL)
- kp_return (*t2);
- else if (*t2 == NIL)
- kp_return (*t1);
- object tmp = KP_TRY (intern (interp, "list", 4));
- if (xcons_p (*t1) && xcar (*t1) == tmp)
- {
- tmp = KP_TRY (last_L (interp, *t1));
- if (xcdr (tmp) == NIL)
- {
- *t2 = KP_TRY (bq_non_splicing (interp, *t2));
- if (xcdr (*t1) == NIL)
- kp_return (*t2);
- else if (xcddr (*t1) == NIL)
- {
- *t2 = KP_TRY (bq_cons (interp, *t1 = xcadr (*t1), *t2));
- kp_return (*t2);
- }
- else
- {
- *t1 = xcdr (*t1), *t2 = KP_TRY (cons::make (interp, *t2, NIL));
- *t1 = KP_TRY (add_LL (interp, *t1, *t2));
- *t2 = KP_TRY (intern (interp, "list*", 5));
- return (cons::make (interp, *t2, *t1));
- }
- }
- }
- if (bq_cons_test (interp, *t1) && xcons_p (*aux = xcadr (*t1)))
- {
- tmp = KP_TRY (last_L (interp, *aux));
- if (xcdr (tmp) == NIL && xcar (*aux) != symbol::comma)
- {
- *t2 = KP_TRY (bq_non_splicing (interp, *t2));
- valref lst = KP_TRY (reverse_L (interp, *aux));
- for (*aux = *t2; *lst != NIL; *lst = xcdr (*lst))
- {
- *t1 = KP_TRY (KP_CALL (interp, list_fct,
- symbol::quote, xcar (*lst)));
- *aux = KP_TRY (bq_cons (interp, *t1, *aux));
- }
- kp_return (*aux);
- }
- }
- *aux = KP_TRY (intern (interp, "concat", 6));
- if (xcons_p (*t2) && xcar (*t2) == *aux)
- return (KP_CALL (interp, list_star, *aux, *t1, xcdr (*t2)));
- else
- return (KP_CALL (interp, list_fct, intern (interp, "concat", 6), *t1, *t2));
- }
- static result<object>
- bq_nconc (interpreter *interp, object f1, object f2)
- {
- valref t2 (interp, f2);
-
- if (f1 == NIL)
- kp_return (f2);
- else if (f2 == NIL)
- kp_return (f1);
-
- valref lst = KP_TRY (intern (interp, "nconcat", 7));
- auto fn = list_fct;
-
- if (xcons_p (*t2) && xcar (*t2) == *lst)
- *t2 = xcdr (*t2), fn = list_star;
- return (KP_CALL (interp, fn, *lst, f1, *t2));
- }
- static result<object>
- bq_append_multi (interpreter *interp, object forms)
- {
- if (forms == NIL)
- kp_return (forms);
-
- bool nc = false;
- valref tf (interp, forms), res (interp, NIL),
- f1 (interp, xcar (*tf)), tmp (interp, NIL);
-
- if (xcons_p (*f1) && xcar (*f1) == BQ_NCONCABLE)
- *res = xcadr (*tf), nc = true;
- else
- *res = *f1;
-
- for (*tf = xcdr (*tf); *tf != NIL; )
- {
- *f1 = xcar (*tf);
- if (xcons_p (*f1) && xcar (*f1) == BQ_NCONCABLE)
- {
- *f1 = xcadr (*f1);
- if (!nc && bq_splicing_p (interp, *res))
- { *tmp = KP_TRY (KP_CALL (interp, list_fct,
- intern (interp, "concat", 6), *res)); }
- else
- *tmp = *res;
-
- *res = KP_TRY (bq_nconc (interp, *f1, *tmp));
- }
- else
- {
- if (nc && bq_splicing_p (interp, *res))
- { *tmp = KP_TRY (KP_CALL (interp, list_fct,
- intern (interp, "nconcat", 7), *res)); }
- else
- *tmp = *res;
-
- *res = KP_TRY (bq_append (interp, *f1, *tmp));
- }
-
- nc = false;
- *tf = xcdr (*tf);
- }
- return (bq_non_splicing (interp, *res));
- }
- static result<object>
- seq_to_cons (interpreter *interp, object seq)
- {
- if (array_p (seq))
- {
- const array *ap = as_array (seq);
- if (ap->len == 0)
- kp_return (NIL);
- KP_VTRY (alloc_cons (interp, ap->len, ap->data, nullptr));
- kp_return (interp->alval);
- }
- else if (table_p (seq))
- {
- valref tmp (interp, NIL);
- for (table::iterator it (interp, seq); it.valid (); ++it)
- { *tmp = KP_TRY (KP_CALL (interp, list_star, it.val (),
- it.key (), *tmp)); }
- return (nreverse_L (interp, *tmp));
- }
- else if (tuple_p (seq))
- {
- valref tmp (interp, NIL);
- for (tuple::iterator it (interp, seq); it.valid (); ++it)
- { *tmp = KP_TRY (cons::make (interp, *it, *tmp)); }
- return (nreverse_L (interp, *tmp));
- }
- kp_return (UNBOUND);
- }
- result<object> expand_bq (interpreter *interp, object form)
- {
- if (form == NIL)
- kp_return (NIL);
- else if (xcons_p (form))
- {
- object tmp = xcar (form);
- if (tmp == symbol::comma)
- kp_return (xcadr (form));
- else if (tmp == symbol::comma_at || tmp == symbol::comma_dot)
- return (bq_nonlist_splice_err (interp, tmp == symbol::comma_dot));
- else if (tmp == symbol::backquote)
- return (KP_CALL (interp, list_fct, symbol::backquote,
- expand_bq (interp, xcadr (form))));
- else
- {
- valref exp = KP_TRY (bq_expand_list (interp, form));
- return (bq_append_multi (interp, *exp));
- }
- }
- else
- {
- valref tmp = KP_TRY (seq_to_cons (interp, form));
- if (*tmp == UNBOUND)
- {
- if (!nksymbol_p (form) && !cons_p (form))
- kp_return (form);
- return (KP_CALL (interp, list_fct, symbol::quote, form));
- }
- *tmp = KP_TRY (expand_bq (interp, *tmp));
- valref app = KP_TRY (intern (interp, "apply", 5));
- if (array_p (form))
- return (KP_CALL (interp, list_fct, *app,
- intern (interp, "array", 5), *tmp));
- else if (table_p (form))
- return (KP_CALL (interp, list_fct, *app,
- intern (interp, "table", 5), NIL, NIL, *tmp));
- else
- return (KP_CALL (interp, list_fct, *app,
- intern (interp, "tuple", 5), NIL, *tmp));
- }
- }
- result<object> reader::read_sexpr (object lbl)
- {
- uint32_t tok = KP_TRY (this->peek ());
- auto ip = this->interp;
- object obj;
- this->take ();
- switch (tok)
- {
- case TOK_NONE:
- kp_return (EOS);
- case TOK_CLOSE:
- return (ip->raise ("parse-error", "read: unexpected ')'"));
- case TOK_CLOSEB:
- return (ip->raise ("parse-error", "read: unexpected ']'"));
- case TOK_CLOSEBRACE:
- return (ip->raise ("parse-error", "read: unexpected '}'"));
- case TOK_DOT:
- return (ip->raise ("parse-error", "read: unexpected '.'"));
- case TOK_SYM:
- case TOK_NUM:
- return (ip->retval);
- case TOK_QUOTE:
- obj = KP_TRY (this->read_sexpr (UNBOUND));
- KP_VTRY (alloc_cons (ip, 2));
- xcar(ip->alval) = symbol::quote;
- xcadr(ip->alval) = obj;
- if (lbl != UNBOUND)
- *this->putlbl(lbl) = ip->alval;
- kp_return (ip->alval);
- case TOK_BQ:
- return (this->read_bq (lbl));
- case TOK_COMMA:
- return (this->read_comma (lbl));
- case TOK_OPEN:
- return (this->read_list (lbl));
- case TOK_DQUOTE:
- return (this->read_str ());
- case TOK_OPENB:
- return (this->read_array (lbl));
- case TOK_OPENBRACE:
- return (this->read_table (lbl));
- case TOK_SHARPOPEN:
- return (this->read_tuple (lbl));
- case TOK_SHARPDQUOT:
- return (this->read_bvector ());
- case TOK_CHAR:
- return (this->read_char ());
- case TOK_SHARPQUOTE:
- {
- obj = KP_TRY (this->read_sexpr (UNBOUND));
- if (!nksymbol_p (obj) && !cons_p (obj))
- return (ip->raise ("parse-error",
- "read: #' must be used with a symbol or list"));
- valref whole = KP_TRY (alloc_cons (ip, 3));
- xcar(*whole) = KP_TRY (intern (ip, "fct", 3));
- // Set arglist.
- xcadr(*whole) = KP_TRY (alloc_cons (ip));
- xcar(xcadr (*whole)) = KP_TRY (gensym (ip, 0, 0));
- // Set body.
- object body = xcar(xcddr (*whole)) = KP_TRY (alloc_cons (ip, 3));
- xcar(body) = KP_TRY (intern (ip, "apply", 5));
- xcadr(body) = obj;
- xcar(xcddr (body)) = xcar (xcadr (*whole));
- kp_return (*whole);
- }
- case TOK_SHARPDOT:
- {
- object obj = KP_TRY (this->read_sexpr (UNBOUND));
- return (eval (ip, obj));
- }
- case TOK_LABEL:
- if (this->getlbl (ip->retval) != UNBOUND)
- return (ip->raise ("parse-error",
- KP_SPRINTF (ip, "read: label %Q redefined",
- ip->retval)));
- obj = ip->retval;
- KP_TRY (this->read_sexpr (UNBOUND));
- *this->putlbl(obj) = KP_TRY (this->read_sexpr (UNBOUND));
- return (ip->retval);
- case TOK_BACKREF:
- obj = ip->retval;
- if ((ip->retval = this->getlbl (obj)) == UNBOUND)
- return (ip->raise ("parse-error",
- KP_SPRINTF (ip, "read: undefined label %Q", obj)));
- return (ip->retval);
- }
- return (ip->retval);
- }
- result<object> reader::read_sexpr ()
- {
- auto ret = this->read_sexpr (UNBOUND);
- if (ret.error_p ())
- {
- this->src->discard ();
- this->take ();
- }
- return (ret);
- }
- reader::~reader ()
- {
- if (this->pairs.data != this->stpairs)
- xfree (this->pairs.data);
- if (this->bufp != this->stbuf)
- xfree (this->bufp);
- }
- // String interpolation.
- static result<int64_t>
- read_fail (interpreter *, stream&, void *, uint64_t)
- {
- return (0);
- }
- static result<object>
- read_from_cstr (interpreter *interp, const void *s, int len, const char **endp)
- {
- stream instrm;
- bvector bv;
- bv.vo_type = typecode::BVECTOR;
- bv.nbytes = len;
- instrm.rdbuf.init ((char *)s, len);
- instrm.cookie = 0, instrm.ilock = UNBOUND;
- instrm.bvec = bv.as_obj ();
- instrm.pos = fixint (0);
- instrm.vo_full = 0;
- instrm.vo_type = typecode::STREAM;
- stream::xops ops;
- ops.read = read_fail;
- instrm.ops = &ops;
- instrm.io_flags = STRM_UTF8 | STRM_READ | STRM_NOLOCK;
- reader rd (interp, instrm.as_obj ());
- object ret = KP_TRY (rd.read_sexpr (UNBOUND));
- if (ret != EOS)
- {
- if (instrm.rdbuf.left () == 0)
- *endp = nullptr;
- else if (isspace (*instrm.rdbuf.curr))
- {
- schar ch;
- KP_VTRY (rd.nextc (ch));
- instrm.ungetuc (ch.buf, ch.len);
- *endp = instrm.rdbuf.curr;
- }
- else
- *endp = instrm.rdbuf.curr;
- }
- return (ret);
- }
- static inline const char*
- xmemchr (const void *p, int ch, size_t len)
- {
- return ((const char *)memchr (p, ch, len));
- }
- static int
- sanitize_fmt (const char *p1, const char *p2)
- {
- chmask mask ("0123456789.$'");
- for (; p1 != p2; ++p1)
- if (!mask.tst (*p1))
- return (*p1);
- return (-1);
- }
- result<object> expand_str (interpreter *interp, object str)
- {
- string *sp = as_str (str);
- auto start = (const char *)sp->data;
- int nb = sp->nbytes;
- auto ptr = xmemchr (start, '$', nb);
- if (!ptr)
- kp_return (str);
- stream *ns = KP_TRY (strstream (interp, deref (alloc_str (interp, 0)),
- STRM_WRITE | STRM_NOLOCK));
- KP_VTRY (ns->write (interp, start, ptr - start));
- valref outs (interp, ns->as_obj ()), args (interp, NIL), elem (interp);
- while (true)
- {
- if (ptr[1] == '$')
- {
- KP_VTRY (ns->putb (interp, '$'));
- const char *tp = xmemchr (ptr += 2, '$', nb -= 2);
- if (!tp)
- {
- KP_VTRY (ns->write (interp, ptr, sp->nbytes - (ptr - start)));
- break;
- }
- KP_VTRY (ns->write (interp, ptr, tp - ptr));
- nb -= tp - ptr, ptr = tp;
- continue;
- }
- const char *p1 = xmemchr (ptr + 1, '{', nb - (ptr + 1 - start));
- if (!p1)
- return (interp->raise ("arg-error", "invalid format string "
- "(expected '{' after '$')"));
- nb = sp->nbytes - (int)(p1 - start);
- const char *p2 = nullptr;
- *elem = KP_TRY (read_from_cstr (interp, p1 + 1, nb - 1, &p2));
- if (!p2 || *p2 != '}')
- return (interp->raise ("arg-error", "invalid format string "
- "(unbalanced '{}' specifiers)"));
- else if (*elem == EOS)
- return (interp->raise ("arg-error", "invalid format string "
- "(incomplete argument inside '${}')"));
- *args = KP_TRY (cons::make (interp, *elem, *args));
- {
- int ch = sanitize_fmt (ptr + 1, p1);
- if (ch >= 0)
- {
- char buf[100];
- sprintf (buf, "invalid format specifier: got %c", (char)ch);
- return (interp->raise ("arg-error", buf));
- }
- }
- KP_VTRY (ns->write (interp, ptr, p1 - ptr - 1),
- ns->write (interp, "%Q", 2));
- ptr = p2 + 1;
- nb = sp->nbytes - (ptr - start);
- ptr = xmemchr (ptr, '$', nb);
- if (!ptr)
- {
- KP_VTRY (ns->write (interp, p2 + 1, nb));
- break;
- }
- KP_VTRY (ns->write (interp, p2 + 1, ptr - p2 - 1));
- }
- if (*args == NIL)
- // Simple string.
- return (sstream_get (interp, ns));
- *args = KP_TRY (nreverse_L (interp, *args));
- // The format string must be quoted so that it's not re-evaluated.
- *elem = KP_TRY (sstream_get (interp, ns));
- *elem = KP_TRY (KP_CALL (interp, list_fct, symbol::quote, *elem));
- *args = KP_TRY (cons::make (interp, *elem, *args));
- *elem = KP_TRY (intern (interp, "%fmt-str"));
- *args = KP_TRY (cons::make (interp, *elem, *args));
- kp_return (*args);
- }
- static result<object>
- backquote_fct (interpreter *interp, object *argv, int)
- {
- return (expand_bq (interp, *argv));
- }
- // (De)serialization definitions.
- pack_cache::pack_cache (interpreter *interp) : ref (interp)
- {
- for (size_t i = 0; i < KP_NELEM (this->st_tab); ++i)
- this->st_tab[i] = UNBOUND;
- this->l_obj.data = this->st_tab;
- this->l_obj.len = (uint32_t)KP_NELEM (this->st_tab);
- this->n_elem = this->n_old = 0;
- this->evict = true;
- *this->ref = this->l_obj.as_obj ();
- }
- static uint32_t
- id_hash (interpreter *interp, object obj)
- {
- int itp = itype (obj);
- switch (itp)
- {
- case typecode::INT:
- case typecode::BIGINT:
- case typecode::FLOAT:
- case typecode::BIGFLOAT:
- case typecode::CHAR:
- return (deref (xhash (interp, obj)));
- default:
- if (itp != typecode::CONS && as_varobj(obj)->flagged_p (FLAGS_CONST))
- return (deref (xhash (interp, obj)));
- return (hash_addr (obj));
- }
- }
- static inline bool
- id_equal (interpreter *interp, object x, object y)
- {
- int t1 = itype (x), t2 = itype (y);
- if (t1 != t2)
- return (false);
- switch (t1)
- {
- case typecode::INT:
- case typecode::BIGINT:
- case typecode::FLOAT:
- case typecode::BIGFLOAT:
- case typecode::CHAR:
- return (deref (equal (interp, x, y)));
- default:
- if (t1 != typecode::CONS &&
- as_varobj(x)->flagged_p (FLAGS_CONST) &&
- as_varobj(y)->flagged_p (FLAGS_CONST))
- return (deref (equal (interp, x, y)));
- return (x == y);
- }
- }
- uint32_t pack_cache::size () const
- {
- return (len_a (*this->ref) / 2);
- }
- object* pack_cache::data ()
- {
- return (as_array(*this->ref)->data);
- }
- static object*
- pcache_getptr (interpreter *interp, pack_cache& cache,
- object key, object mask)
- {
- uint32_t nprobe = 1, idx = id_hash (interp, key) & (cache.size () - 1);
- for (mask = ~mask ; ; )
- {
- object *p = cache.data () + idx * 2;
- if ((*p & mask) == UNBOUND || id_equal (interp, *p & ~EXTRA_BIT, key))
- return (p);
- idx = (idx + nprobe++) & (cache.size () - 1);
- }
- }
- object pack_cache::get (interpreter *interp, object key)
- {
- object *ptr = pcache_getptr (interp, *this, key, 0);
- if (*ptr == UNBOUND)
- return (*ptr);
- else if ((*ptr & EXTRA_BIT) == 0)
- {
- *ptr |= EXTRA_BIT;
- ++this->n_old;
- }
- return (ptr[1]);
- }
- static void
- pcache_cleanup (pack_cache& cache)
- {
- object *data = cache.data ();
- for (uint32_t i = 0; i < cache.l_obj.len; i += 2)
- if (data[i] != UNBOUND && (data[i] & EXTRA_BIT) == 0)
- {
- data[i] = data[i + 1] = UNBOUND | EXTRA_BIT;
- --cache.n_elem;
- }
- }
- static result<void>
- pcache_resize (interpreter *interp, pack_cache& cache)
- {
- uint32_t nsize = cache.size () * 2;
- object *data = cache.data ();
- valref nv = KP_TRY (alloc_array (interp, nsize * 2));
- for (uint32_t i = 0; i < nsize; i += 2)
- {
- object obj = data[i];
- if ((obj & ~EXTRA_BIT) == UNBOUND)
- continue;
- uint32_t nprobe = 1, idx = id_hash (interp, obj) & (nsize - 1);
- while (true)
- {
- if (xaref (*nv, idx * 2) == UNBOUND)
- {
- xaref(*nv, idx * 2 + 0) = obj;
- xaref(*nv, idx * 2 + 1) = data[i + 1];
- break;
- }
- idx = (idx + nprobe++) & (nsize - 1);
- }
- }
- *cache.ref = *nv;
- return (0);
- }
- static const uint32_t CLEANUP_THRESHOLD = 10 * 1024;
- result<void> pack_cache::put (interpreter *interp, object key, object val)
- {
- object *ptr = pcache_getptr (interp, *this, key, EXTRA_BIT);
- if (*ptr != UNBOUND)
- return (0);
- ptr[0] = key, ptr[1] = val;
- if (++this->n_elem * 100 <= this->size () * 75)
- ;
- else if (this->evict && this->n_elem > CLEANUP_THRESHOLD)
- pcache_cleanup (*this);
- else
- return (pcache_resize (interp, *this));
- return (0);
- }
- pack_info::pack_info (interpreter *interp) :
- map (interp), offset (interp), errmsg (nullptr)
- {
- }
- result<void> pack_info::init (interpreter *interp, bool use_cache)
- {
- if (use_cache)
- {
- this->cache = new (this->pcache.ptr ()) pack_cache (interp);
- object base = deref (alloc_bvector (interp, 0));
- this->bstream = KP_TRY (bvstream (interp, base, STRM_RDWR | STRM_NOLOCK));
- }
- else
- {
- this->cache = nullptr;
- *this->map = KP_TRY (KP_CALL (interp, table_fct, NIL, NIL));
- }
- return (0);
- }
- result<void> pack_info::add_mapping (interpreter *interp, object key, object val)
- {
- if (this->cache)
- return (this->cache->put (interp, key, val));
- valref tmp (interp, val);
- object ret = KP_TRY (table_get (interp, *this->map, key, UNBOUND, false));
- if (ret == UNBOUND)
- KP_VTRY (table_put (interp, *this->map, key, *tmp, false));
- return (0);
- }
- object pack_info::get (interpreter *interp, object obj)
- {
- return (this->cache ? this->cache->get (interp, obj) :
- deref (table_get (interp, *this->map, obj,
- UNBOUND, false)));
- }
- void pack_info::touch (interpreter *interp, int offset)
- {
- if (offset < 0)
- return;
- deref (this->bstream->flush (interp));
- as_bvector(this->bstream->extra)->data[offset] |= 0x80;
- }
- result<int64_t> xpack (interpreter *interp, stream *strm,
- object obj, object *map, size_t nmap)
- {
- pack_info info { interp };
- KP_VTRY (info.init (interp, true));
- for (size_t i = 0; i < nmap; i += 2)
- KP_VTRY (info.add_mapping (interp, map[i] | EXTRA_BIT, map[i + 1]));
- int64_t rv = KP_TRY (xpack (interp, info.bstream, obj, info));
- if (rv < 0 || info.bstream->err_p ())
- return (-1);
- uint32_t nsz;
- unsigned char *data = bvstream_data (info.bstream, nsz);
- auto ret = strm->write (interp, data, nsz);
- deref (info.bstream->close (interp));
- return (ret);
- }
- result<void> print_backtrace (interpreter *interp, uint32_t frame,
- stream *strmp, io_info& info)
- {
- KP_VTRY (strmp->write (interp, "Backtrace:\n", 11));
- object tr = KP_TRY (interp->stacktrace (frame));
- if (tr == NIL)
- {
- KP_VTRY (strmp->write (interp, " #<top-level>\n\n", 16));
- return (0);
- }
- uint32_t idx = 0;
- info.flags |= io_info::FLG_SAFE;
- for (cons::iterator it (interp, tr); it.valid (); ++it)
- {
- char sbuf[64];
- object vec = *it;
- uint32_t vl;
- KP_VTRY (strmp->write (interp, sbuf, sprintf (sbuf, " %d: (", idx++)));
- if (!array_p (vec) || (vl = len_a (vec)) == 0)
- {
- KP_VTRY (strmp->write (interp, "???" ")", 4));
- continue;
- }
- object caller = xaref (vec, 0);
- if (fct_p (caller))
- {
- object nm = fct_name (caller);
- if (nm == NIL)
- KP_VTRY (strmp->write (interp, "#:fct", 5));
- else
- KP_VTRY (xwrite (interp, strmp, nm, info));
- }
- else
- KP_VTRY (strmp->write (interp, "???", 3));
- for (uint32_t i = 1; i < vl; ++i)
- {
- KP_VTRY (strmp->putb (interp, ' '),
- xwrite (interp, strmp, xaref (vec, i), info));
- }
- KP_VTRY (strmp->write (interp, ")\n", 2));
- }
- KP_VTRY (strmp->putb (interp, '\n'));
- info.flags &= ~io_info::FLG_SAFE;
- return (0);
- }
- void write_exc (interpreter *interp, stream *strm, object exc, io_info& info)
- {
- if (write_S(interp, strm, type_name (type (exc)), info).error_p () ||
- strm->write(interp, ": ", 2).error_p () ||
- xwrite(interp, strm, exc, info).error_p ())
- {
- static const char msg[] = "failed to write raised value";
- deref (strm->write (interp, msg, sizeof (msg) - 1));
- }
- }
- static int
- do_init_io (interpreter *interp)
- {
- int ret = init_op::call_deps (interp, &init_symbols);
- if (ret != init_op::result_ok)
- return (ret);
- static native_function backquote_macro;
- auto *bp = ensure_mask (&backquote_macro);
- bp->vo_full = function_base::native_flag;
- bp->vo_type = typecode::FCT;
- bp->fct = backquote_fct;
- bp->min_argc = bp->max_argc = 1;
- auto rs = intern (interp, "backquote");
- if (rs.error_p ())
- return (init_op::result_failed);
- object sym = bp->name = deref (rs);
- symval(sym) = bp->as_obj ();
- as_symbol(sym)->set_flag (symbol::ctv_flag);
- return (ret);
- }
- init_op init_io (do_init_io, "io");
- KP_DECLS_END
|